Issues with forwarding of variadics

Based on this thread http://www.cplusplus.com/forum/general/143019/ I could realize my delegate factory, which is working fine. Now it's time to embed those delegates to my IOC Container.

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
// Predefine template delegate factory
template < typename R, typename... Args > 
class brGenericDelegate ;

// C++11 template alias to announce functor definition
template < typename R, typename... Args > 
using brGenericDelegateType = std::function< R(Args...) > ;

class brDelegate
{
protected:
	brDelegate(){}
	
public:
    virtual ~brDelegate() = default ;

    template < typename R, typename... Args >
    static std::shared_ptr<brDelegate> create( typename brGenericDelegate<R,Args...>::functor func ) 
    { 
	return std::make_shared<brGenericDelegate<R,Args...>>(func) ; 
    }
	
    template < typename R, typename... Args > R run( Args... args ) const
    {
        using derived_type = brGenericDelegate<R,Args...> ;
        return dynamic_cast< const derived_type& >(*this)(args...) ;
    }
};

template < typename R, typename... Args > 
class brGenericDelegate : public brDelegate
{
public:
    using functor = brGenericDelegateType< R, Args... >;
    brGenericDelegate( functor f ) : fn(f) {}

    R operator() ( Args... args ) const { return fn(args...) ; }

private:
    const functor fn ;
};



The IOC Container uses a nested wrapper (based on same concept as the delegates) to reference my delegates or instances (if singletons are required) and have to return the requested object on request:

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
// Define generic resolver template
template < class R> 
class GenericResolver;
	
class Resolver
{
protected:
    Resolver(){}
		
 public:
    virtual ~Resolver() = default;
		
    template<typename R, typename ... ARGS>
    std::shared_ptr<R> resolve(ARGS&& ... args) const
    {
	std::cout << "Type to resolve: " << typeid(R).name() << std::endl;
	std::cout << "Args count: " << sizeof...(ARGS) << std::endl;
	using derived_type = GenericResolver<R>;		
	auto rs = dynamic_cast< const derived_type& >(*this);
	rs.run<ARGS...>(std::forward<ARGS>(args)...);
    }		
};
	
template <typename T>
class GenericResolver : public Resolver
{
public:	
	GenericResolver(std::shared_ptr<brDelegate> delegate)
	:m_delegate(delegate){}
		
	GenericResolver(std::shared_ptr<T> instance)
	:m_instance(instance){}

	virtual ~GenericResolver() = default;
		
	template<typename ... ARGS>
	std::shared_ptr<T> run(ARGS&& ... args) const
	{
  	     if(m_instance.get()){
		return m_instance;
	     }
	     else{
		return std::make_shared<T>(
		   m_delegate->run<T, Args...>(std::forward<Args>(args)...));
				m_delegate->run<T, ARGS...>(args ...));
	     } 
	}
		
	std::shared_ptr<brDelegate> getDelegate(void) const {
	     return m_delegate;
	}
		
private:
	std::shared_ptr<brDelegate> m_delegate = nullptr;
	std::shared_ptr<T> m_instance = nullptr;	
};


The IOCContainer has a single resolve method to trigger transient/singleton object requests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T, typename ... ARGS>
inline std::shared_ptr<T> brIOCContainer::resolve(ARGS&& ... args) const
{	
	std::type_index type = typeid(T);
	if(std::is_abstract<T>::value){
		std::string msg = "[brIOCContainer]:resolve: Cant resolve object of abstract type: ";
		msg.append(brStringUtils::demangle(type.name()));		
		throw brRegistrationException(msg);
	}
	
	auto iter = this->find(type);
	if(iter==m_repository.end()){
		std::string msg = "[brIOCContainer]:resolve: No entry found for requested class type: ";
		msg.append(brStringUtils::demangle(type.name()));		
		throw brRegistrationException(msg);
	}
	
	auto resolver = (iter->second)->getResolver();
	return resolver->resolve<T, ARGS...>(std::forward<ARGS>(args)...);			
}


The issue occurs in line 21 of the Resolver snipped. The forwarding of the arguments fails with follwoing output:

In file included from D:/tmp/test/modules/brCore/branches/refactor/include/brCore/brApplication.h:38:0,
                 from d:/tmp/test/modules/brCore/branches/refactor/src/brApplication.cpp:25:
D:/tmp/test/modules/brCore/branches/refactor/include/brCore/brIOCContainer.h: In member function 'std::shared_ptr<_Tp1> binrev::brCore::brIOCContainer::Resolver::resolve(ARGS&& ...) const':
D:/tmp/test/modules/brCore/branches/refactor/include/brCore/brIOCContainer.h:208:15: error: expected primary-expression before '...' token
    rs.run<ARGS...>(std::forward<ARGS>(args)...);
               ^
D:/tmp/test/modules/brCore/branches/refactor/include/brCore/brIOCContainer.h:208:15: error: expected ';' before '...' token
make[2]: *** [CMakeFiles/brCore.dir/src/brApplication.cpp.obj] Error 1
make[1]: *** [CMakeFiles/brCore.dir/all] Error 2


But why? I've tested multiple forwards with this simple code and this works as expected:
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
// used for end of recursion - and for the empty arguments list
void print() { }

template <typename ... Tail>
void print(const std::string& head, Tail&& ... tail)
{
    std::cout << head << '\n';
    print(std::forward<Tail>(tail)...);
}

template < typename M, typename... ARGS > 
inline void doFinal(ARGS&& ... args)
{
    std::cout << typeid(M).name() << std::endl;
    std::cout << "Args count: " << sizeof...(ARGS) << std::endl;
    print(std::forward<ARGS>(args)...);
}

template < typename K, typename... ARGS > 
inline void doSomethingElse(ARGS&& ... args)
{
    doFinal<K, ARGS...>(std::forward<ARGS>(args)...);
}


template < typename R, typename... ARGS > 
inline void doSomething(ARGS&& ... args)
{
    doSomethingElse<R, ARGS...>(std::forward<ARGS>(args)...);
}

template <typename T, typename ... ARGS>
void run(ARGS&& ... args) 
{
    doSomething<T, ARGS...>(std::forward<ARGS>(args)...);
}

int main()
{
  try{ 
     run<int, std::string>(std::string("Hallo Du")); 
  }
  catch(...){
       printf("Unknown exception caught!");
       return APPLICATION_EXIT_ABNORMAL;
  }
  return APPLICATION_EXIT_SUCCESFULLY;
}


I've no idea what is the difference between the simple functions and my object structure. Thanks for any help.
Last edited on
The compiler has not seen the declaration of the member function run() (during phase one of the two phase look up) and proceeds on the assumption that run() is not a template.
Unless you announce that you expect it to be a template.

1
2
// rs.run<ARGS...>(std::forward<ARGS>(args)...);
rs.template run<ARGS...>(std::forward<ARGS>(args)...);
This solves the problem... I've seen this kind of announce in some posts, but never understand why I should use this and when. Thanks a lot!

I would learn to understand why the compiler has lost the declaration of the member function. Could you give me more details or a link, describing the look up mechanism please? Thanks in advance.
Two phase name lookup (somewhat dated, with respect to the GNU and Microsoft implementations): http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html

Legalese (IS, emphasis (underline) added):
When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation, the member template name must be prefixed by the keyword template.

Otherwise the name is assumed to name a non-template.

[Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct X {
    template<std::size_t> X* alloc();
    template<std::size_t> static X* adjust();
};

template<class T> void f(T* p) {

    T* p1 = p->alloc<200>(); // ill-formed: < means less than
    T* p2 = p->template alloc<200>(); // OK: < starts template argument list

    T::adjust<100>(); // ill-formed: < means less than
    T::template adjust<100>(); // OK: < starts template argument list
}

—end example ]


Disambiguating dependent template names (annotation):
http://eli.thegreenplace.net/2012/02/06/dependent-name-lookup-for-c-templates/
Great job! Thanks again for this perfect advise!
Registered users can post here. Sign in or register to post.