views:

143

answers:

3

Hi,

I have atleast 16 functions of the following form.

bool Node::some_walker( Arg* arg1 )
{   
    if(this == NULL)
       return false;

    bool shouldReturn = false;

    if( this->some_walker_p(arg1, shouldReturn) ) //This line alone varies
        return true;

    if( shouldReturn ) // true is already returned
        return false;

    return this->std_walker(arg1);
}

The function some_walker_p is a virtual function and i am not able to templatize it. Is there any solution to avoid this code repetition?

Thanks, Gokul.

+1  A: 

You can use a (non-virtual) template function which calls a virtual (non-template) function to mimic a templated virtual function. That could help, depending on the structure of your code.

Asher Dunn
+4  A: 

It depends on whether the arguments to the private functions are similar or not. The following solutions are possible, ranging from simple and limited to complex and generic:

  1. Equivalent =< Use member-function-pointer)
  2. Same number, different types => Templatize over each argument)
  3. Different numbers/types of arguments => se boost::bind and function objects)

Thanks for the comments given. At first, I only posted the first solution, but there are (as listed) other situations that need different approaches.

Member-function-pointer:

bool Node::walker_caller(Arg* arg1, bool (Node::*memfn)(Arg*, bool))
{   
    ...
    if( (this->*memfn)(arg1, shouldReturn) ) //This line alone varies
        return true;
    ...
}

bool Node::some_walker(Arg* arg1)
{   
    return walker_caller(arg1, &Node::some_walker_p);
}

bool Node::other_walker(Arg* arg1)
{   
    return walker_caller(arg1, &Node::other_walker_p);
}

Sidenote: I usually typedef the mem-fn-ptr to make the syntax more bearable.

Templated arguments:

I assume you always have two arguments here, but they can have different types.

If you have a limited amount of args-numbers (say 1 and 2), you can could implement walker_caller twice, one impl for one-arg and one for two-arg, both templated.

template<class A1, class A2)
bool Node::walker_caller(A1 arg1, A2 arg2, bool (Node::*memfn)(A1, A2, bool))
{   
    ...
    if( (this->*memfn)(arg1, arg2, shouldReturn) ) //This line alone varies
        return true;
    ...
}

bool Node::some_walker(Arg* arg, OtherArg* other_arg)
{   
    return walker_caller(arg, other_arg, &Node::some_walker_p);
}

bool Node::other_walker(OtherArg* other_arg, YetAnotherArg* yaa)
{   
    return walker_caller(other_arg, yaa, &Node::other_walker_p);
}

Function objects:

If your walkers use widely different number and argument types, you probably want to use boost::bind, and maybe boost::function. (Use of the latter is not required but cuts down on the generated code size...)

// faster code, as the function object may be inlined, but
// each call instantiates a different walker_caller, so exe might be bigger
template<class F>
bool Node::walker_caller(const F& fn)
{   
    ...
    if( fn(shouldReturn) ) //This line alone varies
        return true;
    ...
}

// only one implementation, so smaller foot print but
// all arguments need to be copied into a function objet
// which may be a perf hit if the arguments are big
// (this version is good to have when you inherit from Node...)
bool Node::walker_caller(const boost::function<bool (bool)>& fn)
{   
    ...
    if( fn(shouldReturn) ) //This line alone varies
        return true;
    ...
}

bool Node::some_walker(Arg* arg1)
{   
    return walker_caller(boost::bind(&Node::some_walker_p, this, arg1, _1));
}

bool Node::other_walker(Arg* arg1, OtherArg* arg2)
{   
    return walker_caller(boost::bind(&Node::some_walker_p, this, arg1, arg2, _1));
}
Marcus Lindblom
Good solution, but only applicable when all the targets have the same function type.
AndreyT
True. But the OP didn't give much info.
Marcus Lindblom
Thanks, nice soln..
Gokul
If the argument count is the same, a `template<typename ArgType> bool Node::walker_caller(ArgType* arg1, bool (Node::*memfn)(ArgType*, bool ... }` will do fine too and is simplier
Johannes Schaub - litb
@Johannes: True. There are many variations. I'll add that example too, for completeness. :)
Marcus Lindblom
+1  A: 

Use a macro.

I know that get a bad rep, but they have legitimate uses. In general, I think it's more acceptable to use them in your implementation code than in your interface code, so I would consider one here.

#define WALKER_MAYBE_DELEGATE( function_name, attempt, fallback, ArgType) \
void Node::function_name(ArgType arg) {\
...
luispedro
No, we don't want to use macros. But Thanks.
Gokul