Storing std::functions in STL container?

I've a bunch of delegate factories, defined as Lambdas using different arguments, i.e.:

1
2
3
std::function<Test*()> f1 = [&]() -> Test * {return new Test();};
std::function<Test*(const std::string&)> f2 = [&](const std::string& text) -> Test * {return new Test(text);};
std::function<Test*(const std::string&, int)> f3 = [&](const std::string& text, int id) -> Test * {return new Test(text, id);};


How could I define a generic type for those different functions to store them all in same STL map? I've tried to create a template using Variadics for the argument list, but I could not define an Interface for that template with a virtual method to call this delegates with their run time arguments.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<class T, typename ... Args>
class DelegateFactory
{
public:
	std::shared_ptr<T> create(Args&& ... args) const {
		return std::shared_ptr<T>(static_cast<T*>(m_delegate(std::forward<Args>(args)...)));
	}
	
	void registerDelegate(std::function<T* (Args&& ... args)> delegate)
	{
		 m_delegate = delegate;
	}	

private:	
	std::function<T* (Args&& ... args)> m_delegate = nullptr;
};



Is there only one type (Test in the above example) that is involved
or are there factory functions for many different types that are to be stored in the same map?

If it the the second case, how would the different return types be handled?
There are different types and arguments. I have to use those different delegates at my IOC Container. They should be stored in a single map using the type_index as key.
Last edited on
I too wanted to achieve this, in the end I found a method and then used "C++ delegates on steroids" to help make sure it's well rounded.

This method uses two objects, the Event class and Events which manages them. All functs are stored generically as void pointers and are then cast to an appropriate function delegate based on these types upon calling.
Nice article, but I think this solves not the problem. The delegate in the article is also a class template with different types and arguments like my DelegateFactory.

Actually I see no solution to store this templates in a STL container. For the type I could use a void pointer and perform a static cast when I call the delegate, but what is with the arguments?

It looks like a case for boost::any and boost::any_map. But I can't use boost ...
Are you looking for something like this?
(Simple value semantics for arguments are used; its just a rough sketch of a design idea).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <memory>
#include <type_traits>
#include <typeindex>
#include <map>

 struct factory
{
    virtual ~factory() = default ;

    template < typename T, typename... ARGS > static std::shared_ptr<T> create( ARGS... args )
    {
        auto p = get<T>() ;
        return p ? p->create( args... ) : nullptr ;
    }

    factory( const std::type_info& ti ) : key(ti) { map.emplace(key,this) ; }

    private:
        std::type_index key ;
        static std::map< std::type_index, factory* > map ;

        template < typename T > static typename T::factory_type* get()
        {
            const auto iter = map.find( typeid(T) ) ;
            return iter == map.end() ? nullptr : dynamic_cast<typename T::factory_type*>(iter->second) ;
        }
};

std::map< std::type_index, factory* > factory::map ;

template < typename T > struct default_factory : factory
{
    using type = T ;

    default_factory() : factory( typeid(T) ) {}

    template < typename... ARGS > std::shared_ptr<T> create( ARGS... args )
    { return std::make_shared<T>( args... ) ; }
};

template < typename T > struct with_default_factory
{
    using factory_type = default_factory<T> ;
    static factory_type Tfactory ;
    const bool force_instantiation = std::addressof(Tfactory) ;
};

template < typename T > typename with_default_factory<T>::factory_type with_default_factory<T>::Tfactory ;

struct A : with_default_factory<A>
{
    A() { std::cout << "A::default constructor\n" ; }
    A(int) { std::cout << "A::A(int)\n" ; }
    A(int,double) { std::cout << "A::A( int, double )\n" ; }
    ~A() { std::cout << "A::destructor\n" ; }
};

struct B_factory ;
struct B // type with custom factory
{
    using factory_type = B_factory ; // announce factory type
    ~B() { std::cout << "B::destructor\n" ; }
};

struct B_factory : factory // custom factory, must derive from factory
{
    using type = B ; // announce type being created

    B_factory() : factory( typeid(B) ) {} // instantiate base class with type_index of the type

    std::shared_ptr<B> create( const char* msg )
    {
        ++create_cnt ; 
        std::cout << "B_factory::create( '" << msg << "' )\n" ;
        return std::make_shared<B>() ;
    }
    
    std::shared_ptr<B> create( const char* msg1, const char* msg2  )
    {
        ++create_cnt ; 
        std::cout << "B_factory::create( '" << msg1 << "', '" << msg2 << "' )\n" ;
        return std::make_shared<B>() ;
    }
    
    int create_cnt = 0 ;
};

B_factory bfactory ; // instantiate a factory instance (typically, a singleton)

int main()
{
    auto p1 = factory::create<A>() ;
    auto p2 = factory::create<A>(56) ;
    auto p3 = factory::create<A>( 56, 678.9 ) ;
    auto p4 = factory::create<B>( "hello" ) ;
    auto p5 = factory::create<B>( "hello", "world" ) ;
    std::cout << bfactory.create_cnt << '\n' ;
}

http://coliru.stacked-crooked.com/a/db7bf66149daf799
Howdy, what a creazy stuff! This is going in the direction I want to have. But if I understood your snipped correctly I've to create a concrete factory for each Object type which could be created (with the benefit to support different ctor's), right ?

This is what I want to avoid and use Lambda expressions instead. I've implemented an IOC Container, where you actually could register objects in 3 ways:

1. For Transient object resolving : container.register<Mesh>();
2. For Transient object resolving by contract: container.register<Mesh, IMesh>();
3. For Singleton object resolving : container.register<Mesh>(std::make_shared<Mesh>());

This allowes me to register objects without arguments, which could simple resolved with:
auto mesh = container.resolve<Mesh>().

Now I'm at the point, that some Objects have run time dependencies, which should be resolved on request, and which are different on each intance. I.e.: the Buffer:

1
2
3
4
5
     container.resolve<Buffer>(TYPE_STATIC, sizeof(Vertex), 4);
     container.resolve<Buffer>(TYPE_STATIC, sizeof(Vertex), 36);

     or combined
     container.resolve<Mesh>( container.resolve<Buffer>(TYPE_STATIC, sizeof(Vertex), 4));


By this reason I would use a Lambda expression to create an object of a buffer like this and register it as delegate:
 
    container.register<Buffer, Buffer::eType, int, int>([&](Buffer::eType type, int size, int count) -> Buffer * {return new Buffer(type, size, count);};


The resolve should get the correct Lambda expression from map based on resolve type and invoke the delegate with given arguments ...

To use the benefit of an IOC container, there should no extra factory implementations for the registration mechanism.




Topic archived. No new replies allowed.