Josh C
Josh C

Reputation: 1111

Replicate std::function or boost::function?

How can I replicate std::function or boost::function?

I am compiling with C++03 and I am actively avoiding boost.

This started with me trying to treat class methods as regular function pointers. I learned about std::function and boost::function from that endeavour.

Upvotes: 3

Views: 1444

Answers (2)

Felix Glas
Felix Glas

Reputation: 15534

You can create a pre C++11 function pointer to a member function with a declaration like this:

ReturnType (ClassType::*func_ptr)() = &ClassType::function;

and call it using an instance of the class with (instance.*func_ptr)(). E.g.

struct Foo {
    bool memberFunc() { return true; }
};

int main() {
    typedef bool (Foo::*member_func_t)();      // Typedef member func pointer type.
    member_func_t func_ptr = &Foo::memberFunc; // Declare function pointer.
    Foo foo;                                   // Create foo object.
    (foo.*func_ptr)();                         // Call member func using instance.
}

There is a way to recreate the generic properties you are looking for (I read your other question too) if you only need to create pointers to member functions. It will not work if you mix in non-member function pointers.

By using templates and a wrapper class deriving from a common non-template base class you can wrap a member function pointer pointing to a function that is a member of any class type. The base class creates a common type independent of which class the wrapped function pointer is a member of. This can be useful if you e.g. want to store the wrappers in the same container.

This example will work as long as the function signature is always the same (in this case it's fixed as bool()).

struct func_base {
    virtual ~func_base() {};
    virtual bool operator()() const = 0;
};

template <typename C>
class func_wrapper : public func_base {
public:
    typedef bool (C::*member_func_ptr_t)();
    func_wrapper(member_func_ptr_t func_ptr, C* instance_ptr)
        : m_func_ptr(func_ptr), m_instance_ptr(instance_ptr) {}
    bool operator()() const { return (m_instance_ptr->*m_func_ptr)(); }
private:
    member_func_ptr_t m_func_ptr;
    C* m_instance_ptr;
};

You can also create a helper function to create wrappers which automatically deduces the member type.

/* This function returns a pointer to dynamically *
 * allocated memory and it is thus the callers    *
 * responsibility to deallocate the memory!!      */
template <typename C>
func_base* make_wrapper(bool (C::*func_ptr)(), C* instance_ptr) {
    return new func_wrapper<C>(func_ptr, instance_ptr);
}

Now you can use it e.g. like this:

struct Bar { // Define some other class with member function.
    bool memberFunc() { return false; }
};

int main() {
    Foo foo; // Create object instances.
    Bar bar; // ----------||-----------

    std::deque<func_base*> d; // Deque storing pointers to base class.

    // Push pointer to any member func.
    d.push_back(make_wrapper(&Foo::memberFunc, &foo));
    d.push_back(make_wrapper(&Bar::memberFunc, &bar));

    for (std::deque<func_base*>::iterator it = d.begin(); it != d.end(); ++it) {
        (**it)(); // Call all functions in deque.
    }

    for (std::deque<func_base*>::iterator it = d.begin(); it != d.end(); ++it) {
        delete *it; // REMEMBER to destroy wrappers pointed to!!
    }
}

This will compile with C++03 compilers. See this live demo compiled with gcc 4.3.2.

Note: You could easily modify the wrapper class to instead store a copy of the instance if you should want that.

Upvotes: 3

sehe
sehe

Reputation: 393507

You should be able to use TR1's function and bind headers:

#include <tr1/functional>
#include <string>
#include <sstream>

struct AddX {
    template <typename> struct  result { typedef std::string type; };

    std::string operator()(int a, int b) const { 
        std::ostringstream oss;
        oss << (a + b);
        return oss.str();
    }
};

int main() {
    using namespace std::tr1;

    function<std::string(int)> foo = bind(AddX(), 999, placeholders::_1);

    return foo(1).length();
}

Upvotes: 3

Related Questions