I am pretty new to c++ and for my first project am trying to write an ioc container expanding the concept described in this blog post by adding variadic arguments to the registering functions. So one parameter pack would be used for variadic arguments and the other one to declare the dependencies of the type to register.
After reading a bit I came across stuff similar to what I tried to do in the end. The function looks like this:
template <class T,
template<typename ...TDependencies> typename TUnused, typename... TDependencies,
typename ...TArgs>
void RegisterSingletonClassFactory(TArgs...args)
{
std::string typeKey = typeid(T).name();
creatorMap_[typeKey] = [this, args...](){
return new T(GetInstance<TDependencies>()..., args...);
};
std::shared_ptr<IHolder> iHolder = instanceMap_[typeKey];
auto * holder = dynamic_cast<Holder<T>*>(iHolder.get());
if(holder != nullptr)
holder->instance_ = nullptr;
}
And I call it with iocContainer_.RegisterSingletonClassFactory<boost::asio::io_context, std::tuple<>>(30);.
This however gives me the error "error: no matching function for call to"... with clang telling me "candidate template ignored: invalid explicitly-specified argument for template parameter 'TUnused'".
Is there a way to do this? And what exactly does the error mean?
Thanks
And what exactly does the error mean?
The error mean that your call
iocContainer_.RegisterSingletonClassFactory<boost::asio::io_context, std::tuple<>>(30);
doesn't matches the declaration of your template function
template <class T,
template<typename ...TDependencies> typename TUnused, typename... TDependencies,
typename ...TArgs>
void RegisterSingletonClassFactory(TArgs...args)
because your template functions waits for
a type parameter (T),
a template-template parameter (TUnused)
and other template parameters when you pass
a type paramer (boost::asio::io_context)
another type template parameter (std::tuple<>)
If you want to pass std::tuple as template-template parameter, you have to pass it without template parameters (std::tuple, not std::tuple<>).
Given that your TUnused parameter is... well, unused,... I suppose that your intention was to use it as type container.
But there is no needs for it.
Not sure but seems to me that your looking for something similar
template <typename T, typename... TDependencies, typename ...TArgs>
void foo (TArgs...args)
So you can explicit T and a (maybe empty) TDependecies... list of types.
The TArgs... list is deduced from the arguments
The following is a silly, but compiling, C++17 example
#include <iostream>
template <typename T, typename... TDependencies, typename ...TArgs>
void foo (TArgs...args)
{
std::cout << "T:" << typeid(T).name() << std::endl;
std::cout << "TDependecies list:" << std::endl;
((std::cout << "- " << typeid(TDependencies).name() << std::endl), ...);
std::cout << "TArgs list:" << std::endl;
((std::cout << "- " << typeid(TArgs).name() << std::endl), ...);
}
int main()
{
foo<std::string, short, int, long, long long>(0, 1l, 2ll);
}
where T is std::string (the first explicit template parameters), TDependencies... is short, int, long, long long (the following explicit template parameters) and TArgs... is int, long, long long (deduced from the 0, 1l, 2ll arguments).
Observe that you don't need the TUnused template parameter.
It is clear now that std::tuple was not really needed in the original problem, but for completeness let me give an example of how Dependencies could be extracted from the std::tuple-like types1. All we need is an additional level of indirection:
template<class T>
struct wrapper {};
template<class T, template<class...> class Unused,
class... Dependencies, class... Args>
void RegisterSingletonClassFactoryImpl(
wrapper<Unused<Dependencies...>>, Args... args) {
std::string typeKey = typeid(T).name();
creatorMap_[typeKey] = [this, args...]() {
return new T(GetInstance<Dependencies>()..., args...);
};
// ...
}
template<class T, class Unused, class... Args>
void RegisterSingletonClassFactory(Args... args) {
RegisterSingletonClassFactoryImpl<T>(wrapper<Unused>{}, args...);
}
Now
RegisterSingletonClassFactory<T, std::tuple<A, B, C>>()
will call RegisterSingletonClassFactoryImpl() with Dependencies pack being A, B, C.
1 Any other type list
template<class...>
class type_list;
can be used instead of std::tuple.
Related
I think code will better illustrate my need:
template <typename F>
struct return_type
{
typedef ??? type;
};
so that:
return_type<int(*)()>::type -> int
return_type<void(*)(int,int)>::type -> void
I know of decltype and result_of but they need to have arguments passed. I want to deduce the return type of a function pointer from a single template parameter. I cannot add the return type as a parameter, because that's exactly what I want to hide here...
I know there's a solution in boost, but I can't use it, and an attempt to dig it out from boost resulted in a spectacular failure (as it often does).
C++11 solutions welcome (as long as supported in VS2012).
If you can use variadic templates (November '12 CTP), this should work:
template <class F>
struct return_type;
template <class R, class... A>
struct return_type<R (*)(A...)>
{
typedef R type;
};
Live example.
If you can't use variadic templates, you'll have to provide specific specialisations for 0, 1, 2, ... parameters (by hand or preprocessor-generated).
EDIT
As pointed out in the comments, if you want to work with variadic functions as well, you'll have to add one extra partial specialisation (or one for each parameter count in the no-variadic-templates case):
template <class R, class... A>
struct return_type<R (*)(A..., ...)>
{
typedef R type;
};
It has been a while since the question has been asked. For C++17, there is an interesting option. However, the syntax is a bit different from what was originally asked, but the result (a type) is the same.
First, we need a helper function. As you see here, the function accepts a function pointer and returns an object of type R. We need this only for a decltype statement and therefore this function will never be called, so a forward declaration is sufficient.
template<typename R, typename... ARGS>
static R return_type(R (*)(ARGS...)); // forward declaration only for decltype
The trick is to provide the function pointer as a template auto parameter, which is forwarded to a decltype statement:
template<auto FUNCTION_POINTER>
using ReturnType = decltype(return_type(FUNCTION_POINTER));
Now it is easy to get the return type:
#include <iostream>
#include <type_traits>
template<typename R, typename... ARGS>
static R return_type(R (*)(ARGS...)); // forward declaration only for decltype
template<auto FUNCTION_POINTER>
using ReturnType = decltype(return_type(FUNCTION_POINTER));
int func1(char c, int i, long l); // also here only forward declarations needed
void func2(unsigned u, float f);
double func3(bool b);
int main()
{
std::cout << std::is_same_v<int, ReturnType<func1>> << std::endl;
std::cout << std::is_same_v<void, ReturnType<func2>> << std::endl;
std::cout << std::is_same_v<double, ReturnType<func3>> << std::endl;
std::cout << std::is_same_v<void, ReturnType<func1>> << std::endl;
}
You can try the complete example in Wandbox: https://wandbox.org/permlink/5akL0MQDoDuJlQNY
Just wondering why is this invalid:
#include <iostream>
template <std::size_t... Is>
void foo(Is&&... args) {
std::cout << "foo called with " << sizeof...(Is) << "params\n";
}
int main() {
foo(1, 2, 3, 4);
}
It seems a perfectly reasonable example, yet it fails on any compiler I can get my hands on.
If I substitute size_t for class the example works as expected. I've also tried using the new auto template parameter but no online compiler accepts this so I don't know if this an invalid use case or a conformance issue.
It's not valid C++, that's why.
If you instantiate that function template with the template arguments 1, 2, 3, 4 then after substituting the arguments into the template you get the signature:
void foo(1&&, 2&&, 3&&, 4&&);
That's clearly not a valid function.
If you want to write a function template that accepts any number of arguments but only if they are of the right type, you can do that like this in C++17:
template<typename T>
using is_size_t = std::is_same<T, std::size_t>;
template<typename... T>
std::enable_if_t<std::conjunction<is_size_t<T>...>::value>>
foo(T&&... args);
Or alternatively (also using C++17):
template<typename... T>
std::enable_if_t<(std::is_same_v<std::size_t, T> && ...)>
foo(T&&... args);
For C++14 you need to implement std::conjunction yourself, e.g. using the and_ template from p0032r1
I've been working with another language lately and totally pulled a Monika there. Just to complement Jonathan's answer, (thanks for the explanation and the comments) this is how to ensure all parameters are of size_t type (what I was actually trying to do) using concepts:
template <class... Is>
requires (std::is_same<Is, int>::value && ...)
void foo(Is&&... args) { /*...*/ }
Or even (ma fav) by defining a dedicated concept
template <class T> concept bool Integer = std::is_same<T, int>::value;
template <Integer... Is> void foo(Is&&... args) { /*...*/ }
// ^^^^^^^awesome
Live
I think code will better illustrate my need:
template <typename F>
struct return_type
{
typedef ??? type;
};
so that:
return_type<int(*)()>::type -> int
return_type<void(*)(int,int)>::type -> void
I know of decltype and result_of but they need to have arguments passed. I want to deduce the return type of a function pointer from a single template parameter. I cannot add the return type as a parameter, because that's exactly what I want to hide here...
I know there's a solution in boost, but I can't use it, and an attempt to dig it out from boost resulted in a spectacular failure (as it often does).
C++11 solutions welcome (as long as supported in VS2012).
If you can use variadic templates (November '12 CTP), this should work:
template <class F>
struct return_type;
template <class R, class... A>
struct return_type<R (*)(A...)>
{
typedef R type;
};
Live example.
If you can't use variadic templates, you'll have to provide specific specialisations for 0, 1, 2, ... parameters (by hand or preprocessor-generated).
EDIT
As pointed out in the comments, if you want to work with variadic functions as well, you'll have to add one extra partial specialisation (or one for each parameter count in the no-variadic-templates case):
template <class R, class... A>
struct return_type<R (*)(A..., ...)>
{
typedef R type;
};
It has been a while since the question has been asked. For C++17, there is an interesting option. However, the syntax is a bit different from what was originally asked, but the result (a type) is the same.
First, we need a helper function. As you see here, the function accepts a function pointer and returns an object of type R. We need this only for a decltype statement and therefore this function will never be called, so a forward declaration is sufficient.
template<typename R, typename... ARGS>
static R return_type(R (*)(ARGS...)); // forward declaration only for decltype
The trick is to provide the function pointer as a template auto parameter, which is forwarded to a decltype statement:
template<auto FUNCTION_POINTER>
using ReturnType = decltype(return_type(FUNCTION_POINTER));
Now it is easy to get the return type:
#include <iostream>
#include <type_traits>
template<typename R, typename... ARGS>
static R return_type(R (*)(ARGS...)); // forward declaration only for decltype
template<auto FUNCTION_POINTER>
using ReturnType = decltype(return_type(FUNCTION_POINTER));
int func1(char c, int i, long l); // also here only forward declarations needed
void func2(unsigned u, float f);
double func3(bool b);
int main()
{
std::cout << std::is_same_v<int, ReturnType<func1>> << std::endl;
std::cout << std::is_same_v<void, ReturnType<func2>> << std::endl;
std::cout << std::is_same_v<double, ReturnType<func3>> << std::endl;
std::cout << std::is_same_v<void, ReturnType<func1>> << std::endl;
}
You can try the complete example in Wandbox: https://wandbox.org/permlink/5akL0MQDoDuJlQNY
I have a function which takes one parameter with a default value. Now I also want it to take a variable number of parameters and forward them to some other function. Function parameters with default value have to be last, so... can I put that parameter after the variadic pack and the compiler will detect whether I'm supplying it or not when calling the function?
(Assuming the pack doesn't contain the type of that one last parameter. If necessary, we can assume that, because that type is generally not supposed to be known to the user, otherwise it's considered as wrong usage of my interface anyway....)
template <class... Args>
void func (Args&&... args, SomeSpecialType num = fromNum(5))
{
}
No, packs must be last.
But you can fake it. You can detect what the last type in a pack is. If it is SomeSpecialType, you can run your func. If it isn't SomeSpecialType, you can recursively call yourself with your arguments forwarded and fromNum(5) appended.
If you want to be fancy, this check can be done at compile time (ie, a different overload) using SFINAE techniques. But that probably isn't worth the hassle, considering that the "run-time" check will be constant on a given overload, and hence will almost certainly be optimized out, and SFINAE shouldn't be used lightly.
This doesn't give you the signature you want, but it gives you the behavior you want. You'll have to explain the intended signature in comments.
Something like this, after you remove typos and the like:
// extract the last type in a pack. The last type in a pack with no elements is
// not a type:
template<typename... Ts>
struct last_type {};
template<typename T0>
struct last_type<T0> {
typedef T0 type;
};
template<typename T0, typename T1, typename... Ts>
struct last_type<T0, T1, Ts...>:last_type<T1, Ts...> {};
// using aliases, because typename spam sucks:
template<typename Ts...>
using LastType = typename last_type<Ts...>::type;
template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b, T>::type;
template<typename T>
using Decay = typename std::decay<T>::type;
// the case where the last argument is SomeSpecialType:
template<
typename... Args,
typename=EnableIf<
std::is_same<
Decay<LastType<Args...>>,
SomeSpecialType
>::value
>
void func( Args&&... args ) {
// code
}
// the case where there is no SomeSpecialType last:
template<
typename... Args,
typename=EnableIf<
!std::is_same<
typename std::decay<LastType<Args...>>::type,
SomeSpecialType
>::value
>
void func( Args&&... args ) {
func( std::forward<Args>(args)..., std::move(static_cast<SomeSpecialType>(fromNum(5))) );
}
// the 0-arg case, because both of the above require that there be an actual
// last type:
void func() {
func( std::move(static_cast<SomeSpecialType>(fromNum(5))) );
}
or something much like that.
Another approach would be to pass variadic arguments through a tuple.
template <class... Args>
void func (std::tuple<Args...> t, SomeSpecialType num = fromNum(5))
{
// don't forget to move t when you use it for the last time
}
Pros : interface is much simpler, overloading and adding default valued arguments is quite easy.
Cons : caller has to manually wrap arguments in a std::make_tuple or std::forward_as_tuple call. Also, you'll probably have to resort to std::index_sequence tricks to implement the function.
Since C++17 there is way to work around this limitation, by using class template argument deduction and user-defined deduction guides.
This is espactialy useful for C++20 std::source_location.
Here is C++17 demo:
#include <iostream>
int defaultValueGenerator()
{
static int c = 0;
return ++c;
}
template <typename... Ts>
struct debug
{
debug(Ts&&... ts, int c = defaultValueGenerator())
{
std::cout << c << " : ";
((std::cout << std::forward<Ts>(ts) << " "), ...);
std::cout << std::endl;
}
};
template <typename... Ts>
debug(Ts&&...args) -> debug<Ts...>;
void test()
{
debug();
debug(9);
debug<>(9);
}
int main()
{
debug(5, 'A', 3.14f, "foo");
test();
debug("bar", 123, 2.72);
}
Live demo
Demo with source_location (should be available since C++20, but still for compilers it is experimental).
This is coming a bit late, but in C++17 you can do it with std::tuple and it would be quite nice overall. This is an expansion to #xavlours 's answer:
template <class... Args>
void func (std::tuple<Args&&...> t, SomeSpecialType num = fromNum(5))
{
// std::apply from C++17 allows you to iterate over the tuple with ease
// this just prints them one by one, you want to do other operations i presume
std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}
Then, make a simple function to prepare them:
template<typename... Args>
std::tuple<Args&&...> MULTI_ARGS(Args&&... args) {
return std::tuple<Args&&...>(args...);
}
Now you can call the function like this:
func(MULTI_ARGS(str1, int1, str2, str3, int3)); // default parameter used
func(MULTI_ARGS(str1, int1, str2)); // default parameter used
func(MULTI_ARGS(str1, int1, str2, str3, int3, otherStuff), fromNum(10)); // custom value instead of default
Disclaimer: I came across this question as I was designing a logger and wanted to have a default parameter which contains std::source_location::current() and as far as I was able to find, this is the only way that ensures the caller's information is passed accurately. Making a function wrapper will change the source_location information to represent the wrapper instead of the original caller.
I have a variadic class template deriv which derives off variadic class template base.
I have a function template which takes any type T, and an overload for base<Ts...> types;
How can I get the base<Ts...> overload to be used when passing a const deriv<Ts...>&?
Working example below:
#include <iostream>
#include <tuple>
template<typename... Ts>
struct base
{
std::tuple<Ts...> tuple;
};
template<typename... Ts>
struct deriv : base<Ts...>
{
};
//--------------------------------
template<typename T>
void func(const T&)
{
std::cout << "T" << std::endl;
}
template<typename... Ts>
void func(const base<Ts...>&)
{
std::cout << "base<Ts...>" << std::endl;
}
//----------------------------------------
int main()
{
int a;
base <int, double> b;
deriv<int, double> c;
func(a);
func(b);
func(c); // <--- I want func<base<Ts...>> not func<T> to be called here
exit(0);
}
Output from exemplar:
T
base<Ts...>
T
What I want the output to be:
T
base<Ts...>
base<Ts...>
Unless you are ready to re-engineer your code, you cannot, and for a good reason.
Your non-variadic overload of func() is a better match than the variadic version: in fact, when attempting to resolve your function call, the type parameter T for the non-variadic overload will be deduced to be derived<int, double>.
On the other hand, the parameter pack Ts in your variadic overload will be deduced to be int, double. After type deduction, this will practically leave the compiler with these two choices for resolving your call:
void func(const deriv<int, double>&); // Non-variadic after type deduction
void func(const base<int, double>&); // Variadic after type deduction
Which one should be picked when trying to match a call whose argument is of type derived<int, double>?
deriv<int, double> c;
func(c);
Obviously, the first, non variadic overload is a better match.
So how do you get the second overload called instead of the first? You have a few choices. First of all, you can qualify your call by explicitly specifying the template arguments:
func<int, double>(c);
If you do not like that, maybe you can re-think the definition of the non-variadic overload of func(): do you really want it to accept any possible type T? Or are there some types for which you know this overload is not to be invoked? If so, you can use SFINAE techniques and std::enable_if to rule out the undesired matches.
As a further possibility, you can relax a bit the signature of your template function and allow deducing its argument as an instantiation of a certain template class:
template<template<typename...> class T, typename... Ts>
void func(const T<Ts...>&)
{
std::cout << "base<Ts...>" << std::endl;
}
This change alone should fix your program's behavior in the way you want.
UPDATE:
If you want your specialized function template to be invoked only for classes derived from any instance of the base<> class template, you can use the std::is_base_of<> type trait and std::enable_if in the following way:
template<template<typename...> class T, typename... Ts>
void func(
const T<Ts...>&,
typename std::enable_if<
std::is_base_of<base<Ts...>, T<Ts...>>::value
>::type* = nullptr
)
{
std::cout << "base<Ts...>" << std::endl;
}
ADDENDUM:
In those situations where template function overloading won't help with your design, notice that you can always resort to partial template specialization. Unfortunately, function templates cannot be specialized, but you can still exploit class template partial specialization and add a helper function to hide the instantiation of that template. This is how you would rewrite your code:
namespace detail
{
template<typename T>
struct X
{
static void func(const T&)
{
std::cout << "T" << std::endl;
}
};
template<template<typename...> class T, typename... Ts>
struct X<T<Ts...>>
{
static void func(const T<Ts...>&)
{
std::cout << "base<Ts...>" << std::endl;
}
};
}
template<typename T>
void func(const T& t)
{
details::X<T>::func(t);
}
The generic-template overload is a better match, since it requires no conversion (except adding const, which both overloads have).
You can get the base-template overload by adding an explicit cast (example):
func(static_cast<base<int, double> &>(c));
(Alternatively, you could forgo the multiple overloads and instead stick some is_base_of helper logic into your main function template's body.)