2 // DPCollectionEnumeration.h
3 // HigherOrderMessaging
5 // Created by Ofri Wolfus on 11/05/07.
6 // Copyright 2007 Ofri Wolfus. All rights reserved.
8 // Redistribution and use in source and binary forms, with or without modification,
9 // are permitted provided that the following conditions are met:
11 // 1. Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // 2. Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 // 3. Neither the name of Ofri Wolfus nor the names of his contributors
17 // may be used to endorse or promote products derived from this software
18 // without specific prior written permission.
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #import <Foundation/Foundation.h>
33 #if defined(__HOM_FRAMEWORK__)
34 #import <HigherOrderMessaging/DPObjCRuntime.h>
35 #elif defined(__UITESTINGKIT__)
36 #import <UITestingKit/DPObjCRuntime.h>
38 #import "DPObjCRuntime.h" // For DP_EXTERN
41 #if defined(__HOM_FRAMEWORK__)
42 #import <HigherOrderMessaging/DPMessage.h>
48 typedef struct DPEnumerationState DPEnumerationState
;
51 * A callback function invoked before an enumeration is completed.
52 * This function is responsible of releasing the info and items field,
53 * and perform any other needed cleanups.
55 typedef void (*DPEnumeratioStateReleaseCallBack
) (DPEnumerationState
*state
);
57 struct DPEnumerationState
{
58 // The state of the enumeration, usually an index.
59 // Each call to enumerateWithState:objects:count: should
60 // read the state and update it to the new one.
62 // A buffer to the items of the collection. By default,
63 // the buffer points to the same buffer passed by
64 // enumerateWithState:objects:count:. If the collection
65 // doesn't use that buffer, it is responsible for setting
66 // the items poiner to its own buffer. The buffer should
67 // be released by the release callback function.
69 // A private info used by the collection. The collection
70 // free to do whatever it wants with it.
72 // A release callback for the info field and the objects
73 // buffer if the collection created it. The first call (when
74 // state == 0) to enumerateWithState:objects:count: is
75 // responsible for setting the release callback if needed.
76 DPEnumeratioStateReleaseCallBack release
;
80 * @abstract A protocol defining a collection-independent
83 @protocol DPEnumeration
86 * @abstract Given a state and a buffer of objects, this method
87 * returns the next bunch of objects from the receiver.
89 * @discussion This method is repeatedly called during enumeration
90 * in order to get the next bunch of objects. The collection (the
91 * receiver) is responsible for updating the state of the enumeration.
93 * @param state A pointer to a state structure holding the info of
94 * the enumeration. See above for more info of its fields.
96 * @param buff A buffer for placing the objects from the collection in.
97 * A buffer will must always be provided with this method, but the
98 * collection may choose not to use it and use its own buffer. In this
99 * case the collection is responsible for setting the <code>items</code>
100 * field of the enmeration state to its own buffer.
102 * @param buffLen The amount of objects that fit in the buffer.
104 * @result The number of objects returned by the collection.
106 - (unsigned)enumerateWithState
:(DPEnumerationState
*)state
108 count
:(unsigned)buffLen
;
114 * @abstract A special enumerator designed for the <code>each</code> method.
116 @interface DPEnumerator
: NSObject
{
121 * @abstract Returns an enumerator for a given objects buffer.
123 * @param objects A C array of objects.
124 * @param count The size of the objects array.
125 * @param flag A flag indicating whether the enumerator should
126 * free the objects array when deallocating or not.
128 + (id
)enumeratorWithObjects
:(id
*)objects
129 count
:(unsigned)count
130 freeWhenDone
:(BOOL
)flag
;
133 * @abstract Returns a lightweight enumerator for a given array.
135 + (id
)enumeratorForArray
:(NSArray
*)arr
;
138 * @abstract Returns an enumerator for a given collection.
140 + (id
)enumeratorForCollection
:(id
<DPEnumeration
>)collection
;
143 - (NSArray
*)allObjects
;
145 // The following methods are not supported at this time
153 * @abstract A function that's repeatedly called during enumeration to append
154 * objects to the results collection.
156 * @param resultsCollection The collection to append the objects to.
157 * @param state The state of the enumeration.
158 * @param objects An array of objects to append to the collection.
159 * @param count The number of objects in the objects array.
161 typedef void (*DPEnumerationAppendResultsCallBack
) (id resultsCollection
,
162 DPEnumerationState
*state
,
167 * Generic implementations of the collect:, select/rejectWhere:
168 * and findObjectWhere: methods. Use them only if you're adding
169 * support for a new collection.
171 DP_EXTERN
void DPCollectObjects(id
<DPEnumeration
> collection
,
172 id resultsCollection
,
174 DPEnumerationAppendResultsCallBack callback
);
176 DP_EXTERN
void DPSelectObjects(id
<DPEnumeration
> collection
,
177 id resultsCollection
,
178 DPMessage
**messages
,
179 unsigned messageCount
,
181 DPEnumerationAppendResultsCallBack callback
);
183 DP_EXTERN id
DPFindObject(id
<DPEnumeration
> collection
,
184 DPMessage
**messages
,
185 unsigned messageCount
,
188 // Returns whether a given pointer is an enumerated argument or not.
189 // It does not involve any message sending or dereferencing the pointer,
190 // so it's safe for use with any "random" pointer.
191 DP_EXTERN_INLINE BOOL
DPIsEnumeratedArgument(void *ptr
);
195 * @abstract Defines the HOM based enumeration methods.
197 @protocol DPCollectionEnumeration
<DPEnumeration
>
200 * @abstract Sends the passed message to all objects of the
201 * receiver and returns the results.
203 * @discussion If an object doesn't return an object, or it
204 * returns <code>nil</code>, the result is undefined.
205 * This method supports enumerated arguments returned by the
206 * <code>each</code> method.
208 - (id
)collect
:(DPMessage
*)argumentMessage
;
211 * @abstract Returns all objects of the receiver that returned
212 * <code>YES</code> for the last message.
214 * @discussion The messages list must be NULL/nil terminated.
215 * This method supports enumerated arguments returned by the
216 * <code>each</code> method.
218 - (id
)selectWhere
:(DPMessage
*)firstMessage
, ...;
221 * @abstract Returns all objects of the receiver that returned
222 * <code>NO</code> for the last message.
224 * @discussion The messages list must be NULL/nil terminated.
225 * This method supports enumerated arguments returned by the
226 * <code>each</code> method.
228 - (id
)rejectWhere
:(DPMessage
*)firstMessage
, ...;
231 * @abstract Returns the first object of the receiver that returned
232 * <code>YES</code> for the last message.
234 * @discussion The messages list must be NULL/nil terminated.
235 * This method supports enumerated arguments returned by the
236 * <code>each</code> method.
238 - (id
)findObjectWhere
:(DPMessage
*)firstMessage
, ...;
241 * @abstract Returns an enumerated argument that can be passed
242 * to one of the above methods.
244 * @discussion It's also possible to message the returned object
245 * directly, in which case it'll forward the message to all of
247 * For more info, please refer to the HOM paper at
248 * http://www.metaobject.com/papers/Higher_Order_Messaging_OOPSLA_2005.pdf
254 @interface
NSArray (DPCollectionEnumeration
) <DPCollectionEnumeration
>
257 @interface
NSSet (DPCollectionEnumeration
) <DPCollectionEnumeration
>
260 @interface
NSDictionary (DPCollectionEnumeration
) <DPCollectionEnumeration
>
264 @interface
NSObject (DPHOMGoodies
)
267 * @abstract Makes the receiver receive the passed message.
268 * @discussion This method accepts enumerated arguments.
269 * This is the equivalent of the <code>do</code> method
270 * described in Marcel's HOM paper.
272 - (id
)collect
:(DPMessage
*)msg
;
275 * @abstract Makes the receiver receive the passed message.
276 * @discussion The receiver will receive the passed message,
277 * repeating as many times needed for enumerated arguments.
278 * Upon completion, returns the result of the message or the
279 * result of the last message sent if there were any enumerated
282 - (id
)receive
:(DPMessage
*)msg
;
285 * @abstract Makes the receiver receive the passed message
286 * only if it responds to it.
288 * @discussion Only object return values are supported.
289 * For other return types the behaviour is undefined.
290 * NOTE: This method does *NOT* support enumerated arguments.
292 - (id
)ifResponds
:(DPMessage
*)msg
;
298 * And when HOM fails to do what you need, foreach() is always handy :)
299 * In ObjC 1 it'll just send a -objectEnumerator to the collection
300 * while in ObjC 2 it translates to the native for(in) syntax.
302 * Example: foreach (NSString *str, myArr) { [str doSomething]; }
307 Code taken from CocoaDev. See http://www.cocoadev.com/index.pl?ForallMacro for more details.
308 Modified by Ofri Wolfus.
310 #define foreach(element, collection...) \
311 for(id __foreach_macro_element = nil, __foreach_macro_enumerator = [collection objectEnumerator]; \
312 __foreach_macro_element = !__foreach_macro_element ? [__foreach_macro_enumerator nextObject] : nil;) \
313 for (element = __foreach_macro_element; __foreach_macro_element; __foreach_macro_element = nil)
317 // For ObjC 2 we just use the built in for (in) syntax
318 #define foreach(obj, collection...) for(obj in collection)