tags:

views:

152

answers:

2

I have a bunch of templates that are used for rpc and was wondering if there is a way to simplify them down as it repeats it self allot. I know varags for templates is coming in the next standard but can you do default values for templates?

Also is there a way to handle void returning functions as normal functions? Atm i have to separate them and treat them as two different things every where due to templates not picking up void as type.

template <typename R>
R functionCall(IPC::IPCClass* c, const char* name)
{
 IPC::IPCParameterI* r = c->callFunction( name, false );
 return handleReturn<R>(r);
}

template <typename R, typename A>
R functionCall(IPC::IPCClass* cl, const char* name, A a)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a));
 return handleReturn<R>(r);
}

template <typename R, typename A, typename B>
R functionCall(IPC::IPCClass* cl, const char* name, A a, B b)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b) );
 return handleReturn<R>(r);
}

template <typename R, typename A, typename B, typename C>
R functionCall(IPC::IPCClass* cl, const char* name, A a, B b, C c)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c) );
 return handleReturn<R>(r);
}

template <typename R, typename A, typename B, typename C, typename D>
R functionCall(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d) );
 return handleReturn<R>(r);
}

template <typename R, typename A, typename B, typename C, typename D, typename E>
R functionCall(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d, E e)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d), IPC::getParameter(e) );
 return handleReturn<R>(r);
}

template <typename R, typename A, typename B, typename C, typename D, typename E, typename F>
R functionCall(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d, E e, F f)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d), IPC::getParameter(e), IPC::getParameter(f) );
 return handleReturn<R>(r);
}








inline void functionCallV(IPC::IPCClass* cl, const char* name)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false );
 handleReturnV(r);
}

template <typename A>
void functionCallV(IPC::IPCClass* cl, const char* name, A a)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a));
 handleReturnV(r);
}

template <typename A, typename B>
void functionCallV(IPC::IPCClass* cl, const char* name, A a, B b)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b) );
 handleReturnV(r);
}

template <typename A, typename B, typename C>
void functionCallV(IPC::IPCClass* cl, const char* name, A a, B b, C c)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c) );
 handleReturnV(r);
}

template <typename A, typename B, typename C, typename D>
void functionCallV(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d) );
 handleReturnV(r);
}

template <typename A, typename B, typename C, typename D, typename E>
void functionCallV(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d, E e)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d), IPC::getParameter(e) );
 handleReturnV(r);
}

template <typename A, typename B, typename C, typename D, typename E, typename F>
void functionCallV(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d, E e, F f)
{
 IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d), IPC::getParameter(e), IPC::getParameter(f) );
 handleReturnV(r);
}










inline void functionCallAsync(IPC::IPCClass* cl, const char* name)
{
 IPC::IPCParameterI* r = cl->callFunction( name, true );
 handleReturnV(r);
}

template <typename A>
void functionCallAsync(IPC::IPCClass* cl, const char* name, A a)
{
 IPC::IPCParameterI* r = cl->callFunction( name, true, IPC::getParameter(a));
 handleReturnV(r);
}

template <typename A, typename B>
void functionCallAsync(IPC::IPCClass* cl, const char* name, A a, B b)
{
 IPC::IPCParameterI* r = cl->callFunction( name, true, IPC::getParameter(a), IPC::getParameter(b) );
 handleReturnV(r);
}

template <typename A, typename B, typename C>
void functionCallAsync(IPC::IPCClass* cl, const char* name, A a, B b, C c)
{
 IPC::IPCParameterI* r = cl->callFunction( name, true, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c) );
 handleReturnV(r);
}

template <typename A, typename B, typename C, typename D>
void functionCallAsync(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d)
{
 IPC::IPCParameterI* r = cl->callFunction( name, true, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d) );
 handleReturnV(r);
}

template <typename A, typename B, typename C, typename D, typename E>
void functionCallAsync(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d, E e)
{
 IPC::IPCParameterI* r = cl->callFunction( name, true, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d), IPC::getParameter(e) );
 handleReturnV(r);
}

template <typename A, typename B, typename C, typename D, typename E, typename F>
void functionCallAsync(IPC::IPCClass* cl, const char* name, A a, B b, C c, D d, E e, F f)
{
 IPC::IPCParameterI* r = cl->callFunction( name, true, IPC::getParameter(a), IPC::getParameter(b), IPC::getParameter(c), IPC::getParameter(d), IPC::getParameter(e), IPC::getParameter(f) );
 handleReturnV(r);
}

extra code as requested:

template <typename R>
R handleReturn(IPC::IPCParameterI* r)
{
    if (r->getType() == PException::getTypeS())
    {
        gcException gce((gcException*)r->getValue());
        safe_delete(r);
        throw gce;
    }

    R temp = IPC::getParameterValue<R>(r, true);
    safe_delete(r);
    return temp;
}

inline void handleReturnV(IPC::IPCParameterI* r)
{
    if (r->getType() == PException::getTypeS())
    {
        gcException gce((gcException*)r->getValue());
        safe_delete(r);
        throw gce;
    }

    safe_delete(r);
    return;
}
+2  A: 

For variable arguments, there isn't much you can do. You could use Boost.Preprocessor to generate the functions, but if that were an option you might as well use their Boost.Bind library.

Also is there a way to handle void functions as normal functions

You actually can. The following is a-ok:

void foo(void)
{
    // sup
}

void bar(void)
{
    return foo();
}

void baz(void)
{
    return bar();
}

void lolwut(void)
{
    return baz();
}

It just doesn't do anything, return-wise.


So what you'd want is:

// snipping for conciseness, obviously this applies to all variants
template <typename R>
R functionCall(IPC::IPCClass* c, const char* name)
{
    IPC::IPCParameterI* r = c->callFunction( name, false );
    return handleReturn<R>(r);
}

template <typename R, typename A>       // taking as const& to avoid copy
R functionCall(IPC::IPCClass* cl, const char* name, const A& a)
{
    IPC::IPCParameterI* r = cl->callFunction( name, false, IPC::getParameter(a));
    return handleReturn<R>(r);
}

You just call it like normal, and when the return type is void put void as R. The returns will be treated as return;.

The problem then becomes the handleReturn function. You need a compile-time if-statement to route to the correct function based on whether R is void or not. Here's a basic type-traits (as such queries on types are called) framework:

// any constant-expression can be turned into a type
// that can be instantiated, true and false generate
// different types
template <bool B>
struct bool_type
{
    static const bool value = B;
};

// the two fundamental types that will be generated
typedef bool_type<true> true_type; // the expression was true
typedef bool_type<false> false_type; // the expression was false

// mark functions that take a bool_type result...
typedef const true_type& true_tag; // ...as "the result was true"
typedef const false_type& false_tag; // ...or "the result was false"

This is the very core of a type-traits system. Consider:

void foo(true_tag); // B was true
void foo(false_tag); // B was not true

void foo(void)
{
    static const bool B = true;
    foo( bool_type<B>() );
}

Depending on whether B is true or not, we will route to a different version of foo, either the true_tag variant, or the false_tag variant. Make sure you understand this part before moving on.

We now use template specialization to generate a type that inherits from bool_type, and is either a true_type or false_type depending on if that trait is true. For us:

template <typename T> // in general, T is not a void...
struct is_void : bool_type<false>
{
    typedef T type;    
};

template <> // ...but in this case it is
struct is_void<void> : bool_type<true>
{
    typedef void type;
};

Now we can choose a function based off whether or not a certain type is void:

void foo(true_tag); // R was void
void foo(false_tag); // R was not void

template <typename R>
void foo(void)
{
    // either inherits from true_type or false_type
    // and goes to the respective function
    foo( is_void<R>() );
}

Or applied to our situation:

// I put all my detail functions in a namespace called detail,
// whether or not you do the same is up to you
namespace detail 
{
    template <typename R> // not void variant
    R getReturn(IPC::IPCParameterI* r, false_tag)
    {
        R temp = IPC::getParameterValue<R>(r, true);
        safe_delete(r);
        return temp;
    }

    template <typename R> // void variant
    R getReturn(IPC::IPCParameterI*, true_tag)
    {
        // do nothing
    }
}

template <typename R>
R handleReturn(IPC::IPCParameterI* r)
{
    // applies to both
    if (r->getType() == PException::getTypeS())
    {
        gcException gce((gcException*)r->getValue());
        safe_delete(r);
        throw gce;
    }

    // but now route to the correct version
    return detail::getReturn<R>(r, is_void<R>());
}

This code has minimal repetition in concerns to void return types.

GMan
I can't see how your example corresponds with the problem of arbitrary arguments number.+1 For Boost.Bind though.
Basilevs
I was talking about the return type as void is hard to use in the normal template
Lodle
@Basilevs: I was dealing with the question "Also is there a way to handle void functions as normal functions." But I've addressed it all, now.
GMan
wow. I thought i knew allot about templates. This takes it to a whole new level. So you can use templates not only for types but also values in the case of the bool struct?
Lodle
@Lodle: Exactly. And once you do that, you end up with a different type for every value that goes in. (A boolean only takes on two values, though.) We use the fact it makes two distinct types to make those two variants of `bool_type`, which serve to overload a function.
GMan
I would have advised `Boost.enable_if` to handle the choice between the 2 `getReturn` methods instead of rolling your own.
Matthieu M.
@Matthieu: I agree, but I'm assuming Boost isn't available. (If it were, Boost.Bind + some stuff would probably be better than all this.)
GMan
Well, I actually hope Boost is available for I've gone all preprocessor craze :)
Matthieu M.
I think you can handle the second `void` issue with much less code. First, you need a RAII object: `struct raii { IPC::IPCParameterI *r; raii(IPC::IPCParameterI *r):r(r) { } ~raii() { safe_delete(r); } };` And then you write `handleReturn` in this way: `... handleReturn(IPC::IPCParameterI *r) { raii del(r); ... return IPC::getParameterValue<R>(r, true); }`
Johannes Schaub - litb
@Johannes: The reason I split it up that way is because this presumably doesn't work when `R` is `void`: `R temp = IPC::getParameterValue<R>(r, true);`, or the OP wouldn't have split it up for that. Of course, I could be mis-understanding you. (Although your idea does give me other ideas.)
GMan
@GMan, well with the `RAII` object you don't need a `temp` variable. You directly return what is returned by `getParameterValue`, like in your first `void` examples.
Johannes Schaub - litb
@Johnannes: Right, but I'm failing to see how it would work with one function, when R is `void`. Maybe you should edit my answer or post a new one. :)
GMan
+2  A: 

Oh great! Let's have fun :)

It's effectively possible to automatically compute all those templates without varargs. It only requires Preprocessor Programming, so first let's look at Boost.Preprocessor.

First, we need to devise a macro that will take care of the actual function definition:

#define FUNCTION_CALL_IPC(z, n, data)\
  IPC::getParameter( BOOST_PP_CAT(data, n) )

#define FUNCTION_CALL(z, n, data)                               \
  template <                                                    \
    class R                                                     \
    BOOST_ENUM_TRAILING_PARAM(n, class Arg)                     \
  >                                                             \
  R functionCall(IPC::IPCClass* cl, const char* name            \
    BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(n, Arg, const& arg)    \
  )                                                             \
  {                                                             \
    IPC::IPCParameterI* r = cl->callFunction(name, false        \
      BOOST_PP_ENUM_TRAILING(n, FUNCTION_CALL_IPC, arg)         \
    );                                                          \
    return handleReturn<R>(r);                                  \
  }

// From 0 to 9 arguments
BOOST_PP_REPEAT(10, FUNCTION_CALL, ~)

And voila.

Combined with Gman's trick to handle void, you are all set!

Matthieu M.
There you go again with your preprocessor shenanigans. :)
GMan
Wow, Thats crazy except no one would be able to change it. Wish i could award two best answers but ill do you Mat as you have less xp.
Lodle
`@GMan`: I must admit I enjoy playing around with the Preprocessor :) `@Lodle`: accept whatever you wish, I don't care much for xp :p As for maintenance, I admit it might be a bit awkward but the truly bummer is debugging ;) don't forget the `-E` option on `gcc` to get the preprocessor output!
Matthieu M.