tags:

views:

372

answers:

5

I'm looking for an easy-to-use macro for calling a function only once for a specific value. For example:

void foo( Object* obj )
{
   // Print out the name of each object only once
   DO_ONCE( obj, printf("This gets printed only once per object! %s\n",obj->GetName()) );
}

Then

Object obj1("obj1Name"),obj2("obj2Name");
foo(&obj1);
foo(&obj1);
foo(&obj2);

Should print out

This gets printed only once per object! obj1Name
This gets printed only once per object! obj2Name
+3  A: 

Put your objects in a container and filter/group so each one only appears once. This can be done trivially by using set (or std::tr1::unordered_set) as the container for your objects. This effectively makes them unique. You can then iterate over the container.

Or, as others have proposed, use the container inside the function as a memoization device. However, in general I think explicitly grouping the results may be more appropriate.

Konrad Rudolph
+2  A: 

I prefer a map

void foo( Object* obj ){
    // Print out the name of each object only once 
    static std::map<Object*, size_t> calls;
    if(calls[obj] == 0) {
        std::cout << "This gets printed only once per object! "
                  << obj->GetName();
        calls[obj] = 1;
    }
}

You may also decide to increment the counter if you want to count the calls too. But note that it is also not really fail-safe. If you delete an object, and then new it again and it happens to get the same address, it will be assumed to be already printed.

Johannes Schaub - litb
+1  A: 
#include <iostream>
#include <ostream>
#include <set>
#include <string>

class Object
{
public:
    Object( const std::string& name ):
        name_( name )
    {}
    std::string GetName() const
    {
        return name_;
    }
private:
    std::string name_;
};

void print( Object* object )
{
    std::cout << object->GetName() << std::endl;
}

template <typename T, typename TFunction>
void doOnce( T* object, TFunction function )
{
    static std::set<T*> objectsThatWasThere;

    if ( objectsThatWasThere.end() == objectsThatWasThere.find( object ) )
    {
        function( object );
        objectsThatWasThere.insert( object );
    }
}

int main()
{
    Object obj1("Test");
    Object obj2("The");

    doOnce( &obj1, print );
    doOnce( &obj1, print );
    doOnce( &obj1, print );
    doOnce( &obj2, print );
    doOnce( &obj2, print );
    doOnce( &obj2, print );

    return 0;
}
Mykola Golubyev
+1  A: 

You'll probably need to memoize the objects. Something linke

bool do_once( Object * obj )
{
    static std::set<Object*> memo; 
    if ( memo.count(obj) )
    {
        memo.insert(obj);
        return true;
    }
    return false;
}
#define DO_ONCE(o,a) (do_once(obj) && a)
TokenMacGuy
A: 
#include <set>

...

#define DO_ONCE(type, val, stmt) \
do \
{ \
    type __tmp = (val); \
    static std::set < type > __memo; \
    if (__memo.find(__tmp) == __memo.end()) \
    { \
        __memo.insert(__tmp); \
        do { stmt; } while(0); \
    } \
} \
while(0)

...

DO_ONCE(Object *, obj, printf(...));
antti.huima
This will not work, a new std::set<> is created for each invocation of the macro.
Ismael
Now it's not---it's declared static!
antti.huima
Well yes... for each INVOCATION in the code, but isn't that exactly what's needed? If you have multiple routines, you want call each one of them once.
antti.huima
Still will not work, try with your favorite compiler. Because each std::set is in a different scope, they are all different.
Ismael
I've tried this: void f() { int a, b; DO_ONCE(int*, DO_ONCE(int*, DO_ONCE(int*, }And the output is: `123`, where it should be `12`.
Ismael