Gil Hamilton
Gil Hamilton

Reputation: 12347

Adapting constructor to factory requirement

I'm deriving a class from a base class outside my control. The class will get created by a factory function pointer I pass in (in a part of the code I also don't control).

In my derived class, I need to pass an additional constructor argument to the factory function.

My first stab was to try to adapt the factory function via a lambda, but that cannot capture the additional argument. Other answers explained why that doesn't work. Next I've tried to augment that with std::function which other answers have led me to believe would work, but I can't figure out the right syntax and have found the examples to be incomprehensible (and not sure I really understand what that is even doing).

What am I doing wrong here? Is there a better way to solve this?

Demonstration code:

#include <functional>
#include <string>

// I have no control over this
struct Base {
    Base(int i) {}
};

void UseObject(Base *(*factory)(int i)) {
    Base *instance = factory(5);
    // Save created instance
}

// I control the rest
struct Derived : public Base {
    Derived(const char *s, int i) : Base(i) { /* Store s for later use */ }

    static Base *Factory(const char *s, int i) { return new Derived(s, i); }
};

void AddObject(const char *name)
{
    // First stab
    // UseObject([name] (int i) { return Derived::Factory(name, i); });

    // Second stab
    std::function<Base *(int i)> foo { [name] (int i) { return Derived::Factory(name, i); } };
    UseObject(foo);
}

int main(int ac, char **av)
{
    AddObject("some_name");
    AddObject("another_name");
    return 0;
}

The error I get from g++ (7.4.0) is:

tfunc.cpp: In function ‘void AddObject(const char*)’:
tfunc.cpp:28:18: error: cannot convert ‘std::function<Base*(int)>’ to ‘Base* (*)(int)’ for argument ‘1’ to ‘void UseObject(Base* (*)(int))’
 UseObject(foo);

Upvotes: 0

Views: 39

Answers (1)

Jarod42
Jarod42

Reputation: 217283

UseObject doesn't allow non capturing lambda or std::function or regular class functor. The only callable it accepts is function pointer (non capturing lambda can convert to).

So you might do:

UseObject([](int i) { return Derived::Factory("some_name", i); });

but not

auto name = "some_name";
UseObject([name] (int i) { return Derived::Factory(name, i); });

Possible (limited) workaround is to use global variable to store state. (So cannot be used concurrently).

void AddObject(const char *name)
{
    // global usage as UseObject only accepts pointer function
    static const char* instance = nullptr;

    instance = name;
    UseObject(+[](int i) { return Derived::Factory(instance, i); });
}

Upvotes: 1

Related Questions