C++11 Class template method specialization and variadic templates - templates

I am trying to construct the following kind of templated method using GCC and C++11:
class Foo
{
private:
//Device handle
cudnnHandle_t handle_;
//Batch sizes
std::vector<int> batch_;
public:
Foo(cudnnHandle_t handle, std::vector<int> batch) : handle_(handle), batch_(batch) {}
template<typename T, typename... Args> T create(Args&&... args)
{
if(std::is_base_of<Element,T>::value)
return T(handle_,args...);
if(std::is_same<Block,T>::value)
return T(handle_,batch_,args...);
}
};
This however fails to compile due to the return statements. In case where T is Block, return T(handle_,args...) will fail and in case T is a Element base type return T(handle,batch_,args...) will fail.
I tried partial template specialization which is not allowed:
template<typename T, typename... Args> T create(Args&&... args)
{
return T(handle_,args...);
}
template<typename... Args> Block create<Block>(Args&&... args)
{
return Block(handle_,batch_,args...);
}
And i tried full template specialization which is apparently not supported for variadic templates by GCC:
template<typename T, typename... Args> T create(Args&&... args)
{
return T(handle_,args...);
}
template<> Block create<Block,int>(int n)
{
return Block(handle_,batch_,n);
}
So how would i go about creating a function like create?
Kind regards.

Try taking advantage of SFINAE like this
template <typename T, typename... Args,
typename std::enable_if<std::is_base_of<Element, T>::value>::type* = nullptr>
T create(Args&&... args) {
return T(handle_, std::forward<Args>(args)...);
}
template <typename T, typename... Args,
typename std::enable_if<std::is_same<Block, T>::value>::type* = nullptr>
T create(Args&&... args) {
return T(handle_, batch_, std::forward<Args>(args)...);
}

Related

Combing two factory methods returning unique_ptr and shared_ptr into one in C++?

Lets assume I have two factory functions, one returning std::unique_ptr and the other returning std::shared_ptr:
template<class T, class... Args>
std::shared_ptr<T> createObjectS (Args... args)
{
// running some code
return std::make_shared<T>(args...);
}
template<class T, class... Args>
std::unique_ptr<T> createObjectU (Args... args)
{
// running some code
return std::make_unique<T>(args...);
}
Is it possible to combine these two functions into one using template meta programming?
You could use SFINAE, but then I don't really see the point to have this inside a function anymore. It's pretty redundant.
#include <memory>
#include <type_traits>
template <class T, class... Args>
typename std::enable_if<
std::is_same<T, std::shared_ptr<typename T::element_type>>::value, T>::type
createObject(Args&&... args) {
// running some code
return std::make_shared<typename T::element_type>(std::forward<Args>(args)...);
}
template <class T, class... Args>
typename std::enable_if<
std::is_same<T, std::unique_ptr<typename T::element_type>>::value, T>::type
createObject(Args&&... args) {
// running some code
return std::make_unique<typename T::element_type>(std::forward<Args>(args)...);
}
int main() {
auto s = createObject<std::shared_ptr<int>>(1);
auto u = createObject<std::unique_ptr<int>>(1);
}
A little bit more compact but essentially the same idea with a scoped enum
#include <memory>
enum class ptr_t { shared, unique };
template <ptr_t P, class T, class... Args>
typename std::enable_if<P == ptr_t::shared, std::shared_ptr<T>>::type
createObject(Args&&... args) {
// running some code
return std::make_shared<T>(std::forward<Args>(args)...);
}
template <ptr_t P, class T, class... Args>
typename std::enable_if<P == ptr_t::unique, std::unique_ptr<T>>::type
createObject(Args&&... args) {
// running some code
return std::make_unique<T>(std::forward<Args>(args)...);
}
int main() {
auto s = createObject<ptr_t::shared, int>(1);
auto u = createObject<ptr_t::unique, int>(1);
}
In C++17 you of course use if constexpr in both cases rather than SFINAE.
#include <memory>
enum class ptr_t { shared, unique };
template <ptr_t P, class T, class... Args>
decltype(auto) createObject(Args &&... args) {
// running some code
if constexpr (P == ptr_t::shared) {
return std::make_shared<T>(std::forward<Args>(args)...);
} else if (P == ptr_t::unique) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
}
With specialization, you may do:
template <typename T> struct FactoryImpl;
template <typename T> struct FactoryImpl<std::unique_ptr<T>>
{
template <typename ... Ts>
auto operator ()(Ts&&... args) const
{
return std::make_unique<T>(std::forward<Ts>(args)...);
}
};
template <typename T> struct FactoryImpl<std::shared_ptr<T>>
{
template <typename ... Ts>
auto operator ()(Ts&&... args) const
{
return std::make_shared<T>(std::forward<Ts>(args)...);
}
};
template<class T, class... Ts>
auto createObjectS (Ts&&... args)
{
return FactoryImpl<T>{}(std::forward<Ts>(args)...);
}
with usage:
auto s = createObject<std::shared_ptr<MyObject>>(42);
auto u = createObject<std::unique_ptr<MyObject>>(42);

C++ void argument with variadic template

I have the following template in my C++ class:
template <typename ReturnType, typename... Args>
ReturnType run(void* context, const Args&... args) {
/* Do the actual job. */
}
For practical reason, I need to specify it for ReturnType void. I need to keep the list of args because they are still useful.
I tried the following but neither works:
template <typename... Args>
void run(void* context, const Args&... args) {
/* Do the actual job. */
}
and
template <typename ReturnType = void, typename... Args>
void run(void* context, const Args&... args) {
/* Do the actual job. */
}
So.. what is the correct way to specify with the void argument in my example?
In C++17, this is simple: use if constexpr:
template <typename ReturnType, typename... Args>
ReturnType run(void* context, const Args&... args) {
if constexpr (std::is_void_v<ReturnType>) {
/* void case */
} else {
/* non-void case */
}
}
Before C++17, the best way to do this is with tag dispatching. Create a new class template that just encodes the type, so that you can pass it as an argument:
template <typename T> struct tag { };
template <typename ReturnType, typename... Args>
ReturnType run_impl(tag<ReturnType>, void* context, const Args&... args) {
/* non-void case */
}
template <typename... Args>
void run_impl(tag<void>, void* context, const Args&... args) {
/* void case */
}
template <typename ReturnType, typename... Args>
ReturnType run(void* context, const Args&... args) {
return run_impl(tag<ReturnType>{}, context, args...);
}

How to resolve constructor signature in factory function

I want to support one of two possible signatures of the constructor of the class T when creating its instance in the create(...) function below:
template <class Т, typename... Args>
T* create(Special* s, Args&&... args) {
T* t =
// If such a constructor exists, this:
new T(s, std::forward<Args>(args)...);
// Otherwise, this:
new T(std::forward<Args>(args)...);
}
I tried a few monstrous template constructions that did not cut it. The solution for resolving a member function involves SFINAE-failing a decltype of a member function, but this is not apparently possible with a constructor, as it does not have a signature type of its own.
Is this even possible in C++11, and is there any library support?
Just use std::is_constructible:
namespace detail
{
template<typename T, typename... Ts>
auto create(std::true_type, Special* s, Ts&&... args) {
return new T(s, std::forward<Ts>(args)...);
}
template<typename T, typename... Ts>
auto create(std::false_type, Special*, Ts&&... args) {
return new T(std::forward<Ts>(args)...);
}
}
template<class T, typename... Args>
T* create(Special* s, Args&&... args) {
using tag = is_constructible<T, Special*, Args...>;
return detail::create<T>(tag{}, s, std::forward<Args>(args)...);
}
live demo
template <class Т, typename... Args>
T* create_impl(std::true_type, Special* s, Args&&... args) {
return new T(s, std::forward<Args>(args)...);
}
template <class Т, typename... Args>
T* create_impl(std::false_type, Special*, Args&&... args) {
return new T(std::forward<Args>(args)...);
}
template <class Т, typename... Args>
T* create(Special* s, Args&&... args) {
T* t = create_impl<T>(std::is_constructible<T, Special*&, Args&&...>{},
s, std::forward<Args>(args)...);
// ...
return t;
}

Why template instantiations go on forever here?

In the following code, I want to replace
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
with the simple
template <std::size_t N, typename T, typename... Args>
auto check (rank<N,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
But when I do, the template instantiations go on forever despite the terminating check(rank<10, T>, Args... args) function. Here is the full code using the long version above. Sorry for not minimizing the problem because I don't think I can minimize it and show what the problem is. Jumping to main() will show you the simple task I'm after, but I want to solve it using sequence ranking and overload resolution.
#include <iostream>
#include <type_traits>
#include <tuple>
template <typename T, typename... Args>
constexpr auto has_argument_type_impl(int)
-> decltype(std::is_same<typename T::argument_type, std::tuple<Args...>>{}); // Checking both that T::argument_type exists and that it is the same as std::tuple<Args...>.
template <typename T, typename... Args>
constexpr auto has_argument_type_impl(long) -> std::false_type;
template <typename T, typename... Args>
constexpr bool has_argument_type() { return decltype(has_argument_type_impl<T, Args...>(0))::value; }
template <typename T, std::size_t N, typename... Args>
constexpr auto has_argument_type_n_impl(int)
-> decltype(std::is_same<typename T::template argument_type<N>, std::tuple<Args...>>{}); // Checking both that T::argument_type<N> exists and that it is the same as std::tuple<Args...>.
template <typename T, std::size_t N, typename... Args>
constexpr auto has_argument_type_n_impl(long) -> std::false_type;
template <typename T, std::size_t N, typename... Args>
constexpr bool has_argument_type_n() { return decltype(has_argument_type_n_impl<T, N, Args...>(0))::value; }
template <typename... Ts>
class Factory {
template <std::size_t, typename...> struct rank;
template <std::size_t N, typename First, typename... Rest>
struct rank<N, First, Rest...> : rank<N, Rest...> {};
template <std::size_t N, typename T> struct rank<N,T> : rank<N+1, Ts...> {};
template <typename T> struct rank<10, T> {}; // Need to end the instantiations somewhere.
public:
template <typename... Args>
decltype(auto) create (Args... args) const {
return check(rank<0, Ts...>{}, args...);
}
private:
template <typename T, typename... Rest, typename... Args>
auto check (rank<0, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type<T, Args...>(), decltype(T(args...))> {
return T(args...);
}
template <typename T, typename... Rest, typename... Args>
auto check (rank<0, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<0, Rest...>{}, args...))> {
return check(rank<0, Rest...>{}, args...);
}
template <typename T, typename... Args>
auto check (rank<0,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<1, Ts...>{}, args...))> {
return check(rank<1, Ts...>{}, args...); // Since rank<0,T> derives immediately from rank<1, Ts...>.
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type_n<T, N-1, Args...>(), decltype(T(args...))> {
return T(args...);
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, N-1, Args...>(), decltype(check(rank<N, Rest...>{}, args...))> {
return check(rank<N, Rest...>{}, args...);
}
// I want to use the following instead of what's below it.
// template <std::size_t N, typename T, typename... Args>
// auto check (rank<N,T>, Args... args) const
// -> std::enable_if_t<!has_argument_type_n<T, N-1, Args...>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
// return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
// }
//
// template <typename T, typename... Args>
// auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, 0, Args...>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, 1, Args...>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
};
// Testing
struct Object {
template <std::size_t, typename = void> struct ArgumentType;
template <typename T> struct ArgumentType<0,T> { using type = std::tuple<int, bool, char, double>; };
template <typename T> struct ArgumentType<1,T> { using type = std::tuple<bool, char, double>; };
template <std::size_t N> using argument_type = typename ArgumentType<N>::type;
Object (int, bool, char, double) { print(); }
Object (bool, char, double) { print(); }
void print() const { std::cout << "Object\n"; }
};
struct Thing {
template <std::size_t, typename = void> struct ArgumentType;
template <typename T> struct ArgumentType<0,T> { using type = std::tuple<int, int, char>; };
template <typename T> struct ArgumentType<1,T> { using type = std::tuple<int, char>; };
template <typename T> struct ArgumentType<2,T> { using type = std::tuple<char>; };
template <std::size_t N> using argument_type = typename ArgumentType<N>::type;
Thing (int, int, char) { print(); }
Thing (int, char) { print(); }
Thing (char) { print(); }
void print() const { std::cout << "Thing\n"; }
};
struct Blob {
using argument_type = std::tuple<int, double>;
Blob (int, double) { print(); }
void print() const { std::cout << "Blob\n"; }
};
struct Widget {
using argument_type = std::tuple<int>;
Widget (double, double, int, double) { print(); }
Widget (int) { print(); }
void print() const { std::cout << "Widget\n"; }
};
int main() {
Factory<Blob, Object, Thing, Widget>().create(4,3.5); // Blob
Factory<Object, Blob, Widget, Thing>().create(2); // Widget
Factory<Object, Thing, Blob, Widget>().create(5); // Widget
Factory<Blob, Object, Thing, Widget>().create(4,true,'a',7.5); // Object
Factory<Blob, Thing, Object, Widget>().create(true,'a',7.5); // Object
Factory<Blob, Object, Thing, Widget>().create('a'); // Thing
}
I know that there are other ways of accomplishing this, but I'm trying to understand sequence ranking better, and would like to know why I cannot use the commented-out section. How to avoid the repetitive code that I need to put (to rank<9>, or even higher rank) that is currently making this code work? Thanks for your patience.
Note: I actually must NOT enter the repetitive part of the code manually as I currently have. Because the highest N value for rank<N, Ts...> used in the check overloads will be determined during compile-time as the highest N value such that a argument_type<N> member type exists among all the Ts.... Thus I HAVE to use the generic part that I commented out, and the rank<10,T> I'm using will have to have the 10 replaced by that specific N value. Thus, this is not just a matter of convenience. I have to solve this problem to continue developing the program.
Edit: Here is a more minimal example, showing the same problem:
#include <iostream>
#include <type_traits>
#include <tuple>
template <typename T>
constexpr auto has_argument_type_impl(int)
-> decltype(typename T::argument_type{}, std::true_type{});
template <typename T>
constexpr auto has_argument_type_impl(long) -> std::false_type;
template <typename T>
constexpr bool has_argument_type() { return decltype(has_argument_type_impl<T>(0))::value; }
template <typename... Ts>
class Factory {
template <std::size_t, typename...> struct rank;
template <std::size_t N, typename First, typename... Rest>
struct rank<N, First, Rest...> : rank<N, Rest...> {};
template <std::size_t N, typename T> struct rank<N,T> : rank<N+1, Ts...> {};
template <typename T> struct rank<10, T> {}; // Need to end the instantiations somewhere.
public:
template <typename... Args>
decltype(auto) create (Args... args) const {
return check(rank<0, Ts...>{}, args...);
}
private:
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type<T>(), decltype(T(args...))> {
return T(args...);
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<N, Rest...>{}, args...))> {
return check(rank<N, Rest...>{}, args...);
}
template <typename T, typename... Args>
auto check (rank<0,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<1, Ts...>{}, args...))> {
return check(rank<1, Ts...>{}, args...); // Since rank<0,T> derives immediately from rank<1, Ts...>.
}
// I want to use the following instead of what's below it.
// template <std::size_t N, typename T, typename... Args>
// auto check (rank<N,T>, Args... args) const
// -> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
// return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
// }
//
// template <typename T, typename... Args>
// auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
};
// Testing
struct Object {};
struct Thing {};
struct Blob {
using argument_type = std::tuple<int, double>;
Blob (int, double) { std::cout << "Blob\n"; }
};
int main() {
Factory<Object, Thing, Blob>().create(4,3.5); // Blob
}
Partial ordering does not kick in until very late in the overload resolution process.
Ignoring all the ping-ponging amongst your various check overloads, eventually you end up with
template <std::size_t N, typename T, typename... Args>
auto check (rank<N,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, N, Args...>(),
decltype(check(rank<N+1, Ts...>{}, args...))>;
template <typename T, typename... Args>
auto check (rank<10, T>, Args... args) const;
with a rank<10, something I frankly don't care about>. Deduction and substitution will be performed for both overloads; and as part of substitution into the return type of the first signature, you'll instantiate rank<11, Ts...>, which in turn bypasses the terminating specialization of rank, resulting in an infinite chain of template instantiations. You don't even get to the point where the partial ordering tiebreaker chooses the second overload.
Just constrain the first overload to N < 10. It will need to lexically precede the return type (so that when N >= 10 the compiler doesn't attempt to substitute into it), so put it in a default template argument.

How does std::invoke(C++1z) work?

namespace detail {
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template <class Base, class T, class Derived>
inline auto INVOKE(T Base::*pmd, Derived&& ref) ->
decltype(std::forward<Derived>(ref).*pmd) {
return std::forward<Derived>(ref).*pmd;
}
template <class PMD, class Pointer>
inline auto INVOKE(PMD pmd, Pointer&& ptr) ->
decltype((*std::forward<Pointer>(ptr)).*pmd) {
return (*std::forward<Pointer>(ptr)).*pmd;
}
template <class Base, class T, class Derived, class... Args>
inline auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args) ->
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)) {
return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...);
}
template <class PMF, class Pointer, class... Args>
inline auto INVOKE(PMF pmf, Pointer&& ptr, Args&&... args) ->
decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)) {
return ((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...);
}
} // namespace detail
template< class F, class... ArgTypes>
decltype(auto) invoke(F&& f, ArgTypes&&... args) {
return detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
I saw the implementation above from here:
http://en.cppreference.com/w/cpp/utility/functional/invoke
Then I wonder how the compilers match the exact version required. Does SFINAE work on trailing return type?
does SFINAE work on tailing return type?
Yes. The trailing return type syntax doesn't enable any new functionality, it merely makes it easier to write some cases of return types that depend on parameter types.
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...) { ... }
could have equivalently been written as
template <class F, class... Args>
inline decltype(std::forward<F>(std::declval<F&>())(std::forward<Args>(std::declval<Args&>())...))
INVOKE(F&& f, Args&&... args) { ... }
for instance, and the same for all the others. That could be simplified, but even if you simplify it, the fact that the return type cannot use the same syntax as the return expression makes it hard to follow what's going on. Hence the new syntax.
The only time SFINAE doesn't work is for deduced return types. They also use auto, but it's not the auto keyword by itself that disables SFINAE.