4

I am trying to figure out a way to have a container of functors so that I can pass a value into the functors and have it be modified however I am having trouble allowing the functors to not be restricted in what types they can be passed and the amount of arguments they can take.

My actual use for this is that I have a series of functors which all change a 3D vector in some way based on the input. By being able to store these functors in a container I can manipulate the order in which they are called in and end up with a different resulting vector by iterating over the container passing each functor the vector. I'm using these vectors to determine the position, colour, acceleration, etc. of a particle. So ultimately I can create vastly different particle effects by taking this modular approach and eventually I can have the functor order be defined by a file during runtime. That's my final goal :)

The only way I have made this work is with inheritance and a bunch of void pointers which makes the code extremely hard to follow, debug and use.

Here's my code so far that will hopefully demonstrate what I am trying to do better than what I have typed above. Please be aware I am highly out of my comfort zone so this code may be horrible and make some of you gurus want to beat me with a stick.

#include <iostream>
#include <vector>

//the base functor class
struct Functor {
    //an integer so we can tell how many arguments the functor takes
    unsigned num_arguments;
    Functor() : num_arguments(0) {}

    //I'm making the arguments default to 0 so the compiler doesn't complain about not giving enough arguments
    virtual void operator()(void* R, void* A1 = 0, void* A2 = 0, void* A3 = 0) = 0;
};

template<typename R, typename A1>
struct Double : public Functor {
    Double() { num_arguments = 1; }
    void operator()(void* i, void* a, void*, void*) {
        //having to cast everything out of void pointers so it can be used
        A1& first = *static_cast<A1*>(a);

        *static_cast<R*>(i) = first * 2;
    }
};

template<typename R, typename A1, typename A2>
struct Sub : public Functor {
    Sub() { num_arguments = 2; }
    void operator()(void* i, void* a, void* b, void*) {
        //having to cast everything out of void pointers so it can be used
        A1& first = *static_cast<A1*>(a);
        A2& second = *static_cast<A2*>(b);

        *static_cast<R*>(i) = first - second;
    }
};

int main() {
    //our container for the functors
    std::vector<Functor*> functors;
    functors.push_back(new Double<int, int>);
    functors.push_back(new Sub<int, int, int>);

    for(int i = 0; i < functors.size(); ++i) {
        int result;
        int first = 1, second = 2;
        Functor& f = *functors[i];

        if(f.num_arguments == 1) {
            f(&result, &first);
        } else if(f.num_arguments == 2){
            f(&result, &first, &second);
        }

        std::cout << result << std::endl;
    }

    Functor* float_sub = new Sub<float, float, float>;
    float result;
    float first = 0.5f, second = 2.0f;
    (*float_sub)(&result, &first, &second);
    std::cout << result << std::endl;

    functors.push_back(float_sub);

    //The functors vector now contains 3 different types of functors:
    //One that doubles an integer
    //One that subtracts two integers
    //One that subtracts two floats

    std::cin.get();
    return 0;
}

Side note: I am expecting some people to tell me to use so-and-so from the boost libraries. Whilst I appreciate knowing that option is there I would still like to know a better way to implement this without any outside libraries as this is something of a learning exercise for myself.

Edit

Okay so having learnt of stdarg and boost::any I think I can see a way to make this work nicely and am trying it out :)


Solution 2

Okay I have reworked the code using the boost::any and cstdarg for what I think is a better solution. This doesn't use void pointers and doesn't restrict the amount of arguments the functors can have. The newer code also allows you to pass in by value, using void pointers everything had to be by reference which would cause problems trying to do: Sub(&result, 1, 1)

#include <iostream>
#include <vector>
#include <cstdarg>
#include <boost\any.hpp>

struct Functor {
    unsigned num_arguments;
    Functor() : num_arguments(0) {}

    virtual void operator()(boost::any, ...) = 0;
};

template<typename R, typename A1>
struct Double : public Functor {
    Double() { num_arguments = 1; }
    void operator()(boost::any r, ...) {
        R* out = boost::any_cast<R*>(r);

        va_list args;
        va_start(args, r);
        A1 first = va_arg(args, A1);
        va_end(args);

        *out = first * 2;
    }
};

template<typename R, typename A1, typename A2>
struct Sub : public Functor {
    Sub() { num_arguments = 2; }
    void operator()(boost::any r, ...) {
        R* out = boost::any_cast<R*>(r);

        va_list args;
        va_start(args, r);
        A1 first = va_arg(args, A1);
        A2 second = va_arg(args, A2);
        va_end(args);

        *out = first - second;
    }
};

int main() {
    std::vector<Functor*> functors;

    functors.push_back(new Double<int, int>);
    functors.push_back(new Sub<int, int, int>);
    functors.push_back(new Sub<int, float, float>);

    int result = 0;

    for(int i = 0; i < functors.size(); ++i) {
        (*functors[i])(&result, 2, 2);
        std::cout << result << std::endl;
    }

    std::cin.get();
    return 0;
}

and now I finally have the privilege to upvote :D

Lerp
  • 2,957
  • 3
  • 24
  • 43
  • 2
    The better question is "what are you trying to achieve with this". – Cat Plus Plus Dec 20 '11 at 21:04
  • You can do this by using pointer-to-function? Store them? – nikola-miljkovic Dec 20 '11 at 21:11
  • @CatPlusPlus Sorry if it unclear, I am looking for a better way to implement what the above code does or if there is a better way. – Lerp Dec 20 '11 at 21:19
  • That wasn't the question. What do you want it *for*? – Cat Plus Plus Dec 20 '11 at 21:23
  • @CatPlusPlus Sorry, I realized I misunderstood and was just about to edit the comment. The actual use for is so I can make a sort of dynamic function line that will determine the behaviour of a particle. I have edited my original post to include this. – Lerp Dec 20 '11 at 21:24
  • 1
    Have a look through [myWALLJson](http://stackoverflow.com/users/1056328/mywalljson)'s questions, notably ["How to put functions into Boost::variant?"](http://stackoverflow.com/questions/8304582/how-to-store-functional-objects-with-difrent-signature-in-a-container-eg-std/8304694#8304694); I remember he went on a quest for this functionality and he seemed to report back that he found something in that answer. Haven't checked it out, but you may be very interested. – sehe Dec 20 '11 at 22:34
  • I fail to see how a vector of complete different objects can be useful. How would you use then in an iterative manner (a for loop)? Are you looking for a way to apply a sequence of operations to a value? E.g. double it, subtract it by 2, add 4, etc? Sorta like a programmable pipeline for a single value? – deft_code Dec 21 '11 at 02:27
  • I have a series of functors which all change a 3D vector in some way based on the input. By being able to store these functors in a container I can manipulate the order in which they are called in and end up with a different resulting vector by iterating over the container passing each functor the vector. I'm using these vectors to determine the position, colour, acceleration, etc. of a particle. So ultimately I can create vastly different particle effects by taking this modular approach and eventually I can have the functor order be defined by a file during runtime. That's my final goal :) – Lerp Dec 21 '11 at 02:35

2 Answers2

4

Have you thought about using variadic argument lists from the stdarg library? I am not sure it is a better solution, but it is different. For example:

#include <vector>
#include <cstdarg>
#include <iostream>

using namespace std;

template< typename type >
void sub( void* out, ... ){
    // Initialize the variadic argument list.
    va_list args;
    va_start( args, out );

    // Extract our arguments.
    type lhs = va_arg( args, type );
    type rhs = va_arg( args, type );

    // Close the variadic argument list.
    va_end( args );

    // Function logic goes here.
    *(static_cast<type*>(out)) = lhs - rhs;
}

int main( void ){
    typedef void (*functor)( void* out, ... );  // Function type.
    typedef vector< functor > FunctorList;      // Function list type.

    FunctorList fList;
    fList.push_back( &sub<int> );

    int diff;
    fList[0]( &diff, 3, 5 );
    cout << diff << endl;

    return 0;
}
Oz.
  • 5,299
  • 2
  • 23
  • 30
  • `varargs` seem to behave strangely when being used by a member function, at least for me they do? Try turning your `sub` function into a functor. – Lerp Dec 21 '11 at 01:02
  • 1
    @Rarge: I believe your edit suggestion is incorrect -- my documentation says _The argument `last` is the name of the last argument before the variable argument list, that is, the last argument of which the calling function knows the type._ You should probably use `out` in place of `2`, but I could be mistaken on that. (I also don't think the first argument of the function should be a `void *`, but again, I could be mistaken on that.) – sarnold Dec 21 '11 at 01:30
  • 2
    @Oz, the 2nd argument to va_start should be the last argument of known type, `out`. ref: http://linux.die.net/man/3/va_start. – Mark Tolonen Dec 21 '11 at 01:50
  • Well, aren't you just so correct :). Shows how often I use stdarg.h. Strange that it still works perfectly fine with GCC. – Oz. Dec 21 '11 at 01:56
  • @sarnold Oh, I think I understand now. I had misunderstood va_start. I thought the second argument was for the number of arguments the function takes not the last argument before the variable argument list. – Lerp Dec 21 '11 at 02:00
2

I did not realize you specifically wanted function objects (should have clicked from your usage of "functor" and not "function pointer"). Anyways, here is my previous answer using function objects instead. It works just the same:

#include <vector>
#include <cstdarg>
#include <iostream>

using namespace std;

struct FunctorBase {
    virtual void operator()( void* out, ... ) = 0;
}; // end FunctorBase

template< typename T >
struct Subtract : public FunctorBase {
    void operator()( void* out, ... ){
        // Initialize the variadic argument list.
        va_list args;
        va_start( args, out );

        // Extract our arguments
        T lhs = va_arg( args, T );
        T rhs = va_arg( args, T );

        // Close the variadic argument list.
        va_end( args );

        // Function logic goes here.
        *(static_cast<T*>(out)) = lhs - rhs;
    }
};

int main( void ){
    typedef vector< FunctorBase* > FunctorList; // Function list type.

    FunctorList fList;
    fList.push_back( new Subtract<int>() );

    int diff;
    (*fList[0])( &diff, 3, 5 );
    cout << diff << endl;

    return 0;
}
Oz.
  • 5,299
  • 2
  • 23
  • 30
  • When I run this the output is not the expected -2, instead I get -862661424, which is what I was trying to say in my last comment. The va_args seems to behave strangley in member functions? – Lerp Dec 21 '11 at 01:57
  • Hrm, I am compiling this using GCC 4.4.3 on Ubuntu and it works just fine, outputting -2. What compiler are you using? And what error do you get? – Oz. Dec 21 '11 at 02:00
  • Nevermind, the output was because I was misunderstanding va_start. The edited version works fine. – Lerp Dec 21 '11 at 02:03