Exploring Solution Spaces © Copyright 2003-2006, by C. Keith Ray
   


About
Exploring Solution Spaces, Keith Ray's blog on Software development and other topics.

Send comments to:
keithray@mac.com

For Agile Training, eLearning, or Coaching contact:
Industrial Logic, Inc.
866-540-8336 (toll free)
510-540-8336 (Berkeley, California)

Links
xpminifaq
Résumé
“Adopting XP” Article 2002 (pdf)
“ Refactoring” Article 2006
AYE Conference
Lucien W. Dupont
Elisabeth Hendrickson
Johanna Rothman's Managing Product Development
Brian Marick's Exploration Through Example
Esther Derby's Insights You Can Use
Laurent Bossavit's Incipient(thoughts)
Dale Emery's Conversations with Dale
Martin Fowler's Bliki
Creating Passionate Users

Archives

  • 2003
  • 2004
  • 2005
  • 2006
  • 2007
  • 2008
  • Subscribe
    RSS Exploring Solution Spaces XML


           
    2007.Mar.24 Sat

    Work-Arounds for C++'s Lacking Block Syntax

    Doing a simple operation on each item in container...

    In Smalltalk, an arbitrary block of code can be passed to a method of a container. In the example below, "[" and "]" delimit a block of code, which is passed to the method named "do:" of the class List.

    testDo
        | alist aLocal |
        alist := List new.
        "...add Item instances, etc. to alist skipped."
        alist do: [ | item | item doSomething ].
    

    In Ruby, a block of code can be passed to a method using "{" and "}" to delimit the code, or "do" and "end". The method in this case is named "each" and belongs to the List class.

    def testEach
       alist = List.new
       # add Item instances, etc. to alist skipped.
       alist.each { | item | item.doSomething }
    end
    

    In C++ the are no code-blocks that can be passed around like other objects. There are several alternatives which involve writing a function or a class and then... Passing the class as a type-specialization of a template, or passing an instance of a class to have a member function invoked, or passing a function name as a specialization of a template, or passing a function pointer or a passing a pointer-to-a-member-function and a pointer to an object. The first two examples here are using a function and a functor class with the for_each template function.

    #include <vector>
    #include <algorithm>
    
    class Item
    {
    public:
        // etc...
        void doSomething();
    };
    
    void doSomething(Item& anItem)
    {
        anItem.doSomething();
    }
    
    struct Functor
    {
        void operator()(Item& anItem)
        {
            anItem.doSomething();
        }
    };
    
    void testForEachWithFunctionAndFunctor()
    {
        vector<Item> alist; 
        // add Item instances, etc. skipped.
        for_each(alist.begin(), alist.end(), doSomething);
        for_each(alist.begin(), alist.end(), Functor());
    }
    

    One of the drawbacks of the function or functor approach is you can't easily refer to local variables, like the Smalltalk and Ruby examples can do. (Not shown above to keep everything simple.) And the function tends to end up at an inconvenient distance away from the code that uses it do the iteration. An alternative is to use a static function declared in a class, which you can define inside your function, but accessing local variables is still inconvenient. (And for some reason, a functor class defined inside a function doesn't compile. Anyone know why?)

    #include <vector>
    #include <algorithm>
    
    class Item { ... };
    
    void testForEachWithClassStatic()
    {
        vector<Item> alist; 
        // add Item instances, etc...
    
        class Function
        {
            static void doSomething(Item& anItem)
            {
                anItem.doSomething();
            }
        };
        for_each(alist.begin(), alist.end(), Function::doSomething);
    }
    /*
    void testDoesntCompile()
    {
        struct FunctorInternal
        {
            void operator()(Item& anItem) 
            {
                anItem.doSomething();
            }
        };
        for_each(alist.begin(), alist.end(), FunctorInternal()); // error
    }
    */
    

    So if you want to refer to local variables easily, and have the code where you expect it, there's the old for-loop:

    #include <vector>
    #include <algorithm>
    
    class Item { ... };
    
    void testForLoop()
    {
        vector<Item> alist; 
        // add Item instances, etc...
        for (vector<Item>::iterator it = alist.begin();
             it != alist.end(); ++it )
        {
            it->doSomething();
        }
    }
    

    However, that can get verbose if the types and variables involved have long names. The last alternative to simulate passing a bit of code around is to get the preprocessor involved. In these examples, a macro can avoid the explicit 'for' loop as long as the action to do in the loop is simple enough. (And you can still access local variables.)

    #define APPLY4(vectorType,vectorName,iteratorName,Action)            \
        for ( vectorType::iterator iteratorName = vectorName.begin();    \
            iteratorName != vectorName.end(); ++iteratorName)            \
        {                                                                \
            (Action);                                                    \
        }
    
    
    #define APPLY3(vectorName,iteratorName,Action)                    \
        {                                                             \
            typedef typeof(vectorName) vectorType;                    \
            typedef vectorType::iterator itType;                      \
            for ( itType iteratorName = vectorName.begin();           \
                iteratorName != vectorName.end(); ++iteratorName)     \
            {                                                         \
                (Action);                                             \
            }                                                         \
        }
    // NOTE: "typeof" used above is non-standard/non-portable. 
    // (it's in the version of gcc C++ that I'm using on MacOS X.)
    
    void testApplyMacros()
    {
        vector<Item> alist; 
        // add Item instances, etc...
    
        APPLY4(vector<Item>, alist, each, each->doSomething);
    
        APPLY3(alist, each, each->doSomething);
    }
    

    By the way, the APPLY macros work on more than just vectors -- any container that implements "iterator". Making a APPLY_CONST macro that works with "const_iterator" should be trivial.

    [/docs] permanent link