In C++ 11, can I build a template which explicitly instantiates a sequence of function templates into an array initialiser? What I want to achieve (please don't ask why I need that, because it's a long story) looks like
// The template I want to instantiate:
template<size_t N> void callbackTemplate(const int dummy) {
// Do something which needs to know 'N'.
}
// The variable I want to assign 'callbackTemplate' instantiations to:
void (*callback)(const int dummy); // This cannot be std::function, because it
// is not defined by me! It is already there and
// must be used as it is.
// This is the set of instantiations I want to prepare:
std::array<decltype(callback), 3> callbackTemplates = {
callbackTemplate<0>, callbackTemplate<1>, callbackTemplate<2>
};
The code above works as it is. What I want to change is the initialisation of the callbackTemplates array. I want the initialiser to become a template which is dependent on a compile-time constant and creates the instantiations 0..N.
The problem is somehow related to C++ static const array initialization in template class, but I did not manage to "templatise" the instantiation of the other template.
You can do this using an implementation of C++14's std::integer_sequence. Here's one.
With callback_t defined as
using callback_t = decltype (callback);
you can just pass a sequence and make use of the type deduction:
template<unsigned... Is>
array<callback_t, sizeof...(Is)> make_callback_array_impl(seq<Is...>)
{
return { callbackTemplate<Is>... };
}
template<unsigned N>
array<callback_t, N> make_callback_array()
{
return make_callback_array_impl(GenSeq<N>{});
}
live demo
This can be done with variable template specializations.
template<class>
int callbackTemplates_v;
template<std::size_t... Indicies>
std::array<decltype(callback), sizeof...(Indicies)>
callbackTemplates_v<std::index_sequence<Indicies...>> = {callbackTemplate<Indicies>...};
template<std::size_t N>
auto callbackTemplates = callbackTemplates_v<std::make_index_sequence<N>>;
Now callbackTemplates<N> is an array of N instaniations of callbackTemplate from 0..N-1.
Do you mean something as follows?
template <std::size_t ...>
struct range
{ };
template <std::size_t N, std::size_t ... Next>
struct rangeH
{ using type = typename rangeH<N-1U, N-1U, Next ... >::type; };
template <std::size_t ... Next >
struct rangeH<0U, Next ... >
{ using type = range<Next ... >; };
template <std::size_t N>
struct arrayWrapper
{
private:
std::array<decltype(callback), N> callBT;
template <std::size_t ... rng>
arrayWrapper (const range<rng...> &)
: callBT{ callbackTemplate<rng>... }
{ }
public:
arrayWrapper ()
: arrayWrapper (typename rangeH<N>::type())
{ }
};
If you can use C++14, you can throw away rangeH and range and the wrapper become simply
template <std::size_t N>
struct arrayWrapper
{
private:
std::array<decltype(callback), N> callBT;
template <std::size_t ... rng>
arrayWrapper (const std::index_sequence<rng...> &)
: callBT{ callbackTemplate<rng>... }
{ }
public:
arrayWrapper ()
: arrayWrapper (std::make_index_sequence<N>())
{ }
};
Related
Lets say i have a class looking like this
template<int n>
class A{
array<size_t, n> sizes;
//...
public:
template <int k>
A<k> reshape(array<size_t, k> new_sizes){
return A<k>(new_sizes):
}
};
it works but the parameter new_sizes is syntatically suboptimal, since i have to call it like that:
foo.reshape(array<size_t, 3>{1,2,3});
This does not work:
foo.reshape({1,2,3});
Is there a way to either define a initializer_list with compile time size (so i could use it instead of the array) OR (even better) a way to define the size of a variadic parameter, so i could write something like
foo.reshape(1,2,3);
OR (even better) a way to define the size of a variadic parameter, so i could write something like
foo.reshape(1,2,3);
You could take the sizeof... a parameter pack:
template <size_t N>
class A {
std::array<size_t, N> sizes;
public:
A() = default;
template <class... Args>
A(Args&&... ss) : sizes{static_cast<size_t>(ss)...} {}
template <class... Args>
A<sizeof...(Args)> reshape(Args&&... new_sizes) {
// ^^^^^^^^^^^^^^^
return A<sizeof...(Args)>(static_cast<size_t>(new_sizes)...);
}
};
// deduction guide:
template <class... Args>
A(Args&&...) -> A<sizeof...(Args)>;
Demo
{1, 2, 3} has no type but can be deduced as initializer_list<T> or T[N]
So to keep your syntax, it would be:
template <std::size_t K>
A<K> reshape(/*const*/ size_t (&new_sizes)[K])
{
return A<K>(new_sizes):
}
so i could write something like foo.reshape(1,2,3);
For that variadic template:
template <typename... Ts>
A<sizeof...Ts> reshape(Ts... elems)
{
return reshape({elems...}); // using above method
}
Is there any way to call a class member function that takes only 1 template argument instead of 2?
I would like to write some code like this:
template<typename T, size_t N>
void Container<int, N>::quick_sort() {
}
You cannot partial specialize a method, you could partial specialize the whole class, but require some duplication.
template<typename T, size_t N>
class Container
{
// Some code ...
void quick_sort();
};
template <typename T,size_t N>
void Container<T, N>::quick_sort()
{
// ...
}
// Class specialization
template <size_t N>
class Container<int, N>
{
// Some similar/same code...
void quick_sort();
};
template <size_t N>
void Container<int, N>::quick_sort()
{
// ...
}
As alternative, C++17 allows
template<typename T, size_t N>
class Container
{
// Some code ...
void quick_sort()
{
if constexpr (std::is_same_v<int, T>) {
// ...
} else {
// ...
}
}
};
For prior versions, regular if would probably produces error (both branches should be valid, even if not taken).
So tag dispatching is an easy approach (SFINAE is another one):
template <typename> struct Tag{};
template<typename T, size_t N>
class Container
{
private:
void quick_sort(tag<int>)
{
// ...
}
template <typename U>
void quick_sort(tag<U>)
{
// ...
}
public:
void quick_sort()
{
quick_sort(Tag<T>());
}
// Some code ...
};
If I have a class that has two template parameters, is there any way to branch an if constexpr on just one of those parameters? In the following example I can test if both parameters match, but I would like a single way of matching any version of MyTemplateClass that has char as its first parameter.
#include <iostream>
#include <type_traits>
template<typename T,int32_t N>
class MyTemplateClass
{
};
template<typename C>
void DoStuff(const C& myObj)
{
if constexpr(std::is_base_of_v<MyTemplateClass<char,64>, C>) // how do I remove the hardcoded 64?
{
// test passed!
}
else
{
static_assert(false);
}
}
int main()
{
MyTemplateClass<char, 64> passesObj;
MyTemplateClass<char, 128> shouldPassObj;
MyTemplateClass<wchar_t, 64> failsObj;
DoStuff(passesObj); // passes correctly
DoStuff(shouldPassObj); // currently fails, want to make pass
DoStuff(failsObj); // correctly fails
}
You're too focused on new language features instead of old techniques. If you want a template function that only works if the type given is any instance of MyTemplateClass which takes as its first parameter char, then write that:
template<int32_t N>
void DoStuff(const MyTemplateClass<char, N> &myObj)
{
// test passed!
}
This is standard template argument deduction, not C++17 if constexpr gymnastics. This will fail to be called for any type other than one generated from the MyTemplateClass template instantiated with char.
It's possible to have a more generalized solution using template parameter packs, but since all parameters in a pack must be of one kind of template parameter (type, value, or template), you wouldn't be able to mix type and value template parameters in the same pack:
template<typename ...Args>
void DoStuff(const SomeClass<known, params, ...Args> &myObj);
That would only work if the extra arguments are type parameters, not value or template parameters. You could make Args a value parameter pack with auto, but then they couldn't be types. C++ has no mechanism to make a template parameter of known kind.
If DoStuff() receive only MyTemplateClass objects, you can use template deduction
template <typename T, std::int32_t N>
void DoStuff (MyTemplateClass<T, N> const & myObj)
{
if constexpr ( std::is_same_v<char, T> )
{
// test passed!
}
else
{
// not passed
}
}
Another solution could be add a constexpr value in MyTemplateClass
template <typename T, std::int32_t N>
class MyTemplateClass
{ using type = T };
template<typename C>
void DoStuff(const C& myObj)
{
if constexpr ( std::is_same_v<typename C::type, char> )
{
// test passed!
}
else
{
// not passed
}
}
A third solution could be a custom type-trait to extract N
template <typename>
struct getT;
template <typename T, std::int32_t N>
struct getT<MyTemplateClass<T, N>
{ using type = T };
template<typename C>
void DoStuff(const C& myObj)
{
if constexpr ( std::is_same_v<typename getT<C>::type, char> )
{
// test passed!
}
else
{
// not passed
}
}
I need to create a template class depending on a simple struct like this. It has a method AddNew to insert new element for its tuple member.
struct Type
{
int i;
char c;
};
template<typename ...T>
class A
{
std::tuple<T...> ts;
public:
A(T&&...t):ts(std::move(t)...){}
void AddNew(T t)
{
ts=std::tuple_cat(ts,t);
}
};
int main()
{
A<Type,Type,Type,Type> a({1,'a'},{2,'t'},{3,'c'},{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
Problem I don't want to write down Type... 4 times like in main. I like something like A<Type,4> when I initialize A with 4 Types or A<Type, 100> for 100 Types.
You probably just want vector:
struct Type
{
int i;
char c;
};
template<typename T>
class A
{
std::vector<T> data;
public:
template <typename ... Ts>
A(Ts&&...ts) : data(std::forward<Ts>(ts)...){}
void AddNew(const T& t)
{
data.push_back(t);
}
};
int main()
{
A<Type> a(Type{1,'a'}, Type{2,'t'}, Type{3,'c'}, Type{4,'g'});
Type t{5,'x'};
a.AddNew(t);
}
you may partially specialize A for some helper 'tag' type:
template<typename T, std::size_t N>
struct repeat{};
template<typename... T>
struct repeat_unpack{ using type = A<T...>; };
template<typename T, int N, typename... V >
struct repeat_unpack<repeat<T,N>,V...>: std::conditional_t<(N>1),
repeat_unpack<repeat<T,N-1>,T,V...>,
repeat_unpack<T,V...>
>{};
template<typename T, int N>
class A<repeat<T,N>>: repeat_unpack<repeat<T,N>>::type {};
// to be used as
A<repeat<Type,3>>
the above uses inheritance to avoid code duplication, so you'll probably need to lift some members (eg. constructors) to the partial specialization as well.
Otherwise, just use an alias
template<class T, std::size_t N>
using A_nth = typename repeat_unpack<repeat<T,N>>::type;
A_nth<Type,3>
Just one more thing, given that you're using tuple internally, it should be easy to simply specialize for std::array and use it instead, being tuple compatible ...
PS. obviously, your AddNew() code makes no sense as is, I'd assume it's just a code snippet typo ...
Suppose I have a compile-time constexpr array and a variadic class template with a set of non-type parameters of the same type as the elements of the array.
My objective is to instantiate the class template with the values from the array:
struct Container
{
int containee[3];
};
constexpr Container makeContainer();
template <int... Elements> class Foo;
Foo<makeContainer().containee[0],
makeContainer().containee[1],
makeContainer().containee[2]> foo;
The above code works well. However, I'm quite unhappy about having to manually index the array whenever I need to instantiate the Foo template. I would like the compiler to do that for me automatically:
Foo<Magic(makeContainer().containee)> foo;
I did some RTFM at cppreference, but that didn't help. I'm aware of std::forward<>(), but it cannot be applied to template argument lists.
Change makeContainer to a struct with a constexpr operator() or a constexpr lambda (C++17). A function pointer will not work here.
struct makeContainer
{
constexpr auto operator()() const
{
return Container{/* ... */};
}
};
Use std::make_index_sequence and std::index_sequence to generate a compile-time sequence of the indices:
template <typename C>
constexpr auto fooFromContainer(const C& container)
{
return fooFromContainerImpl(container, std::make_index_sequence<3>{});
}
Create a new constexpr container instance through C, then expand the sequence to index the elements in a constant expression:
template <typename C, std::size_t... Is>
constexpr auto fooFromContainerImpl(const C& container, std::index_sequence<Is...>)
{
constexpr auto c = container();
return Foo<c.containee[Is]...>{};
}
complete example on wandbox.org
Just for fun, here's a C++20 implementation:
struct container { int _data[3]; };
template <int... Is>
struct foo
{
constexpr auto t() const { return std::tuple{Is...}; }
};
template <typename C>
constexpr auto foo_from_container(const C& c)
{
return []<std::size_t... Is>(const auto& c, std::index_sequence<Is...>)
{
return foo<c()._data[Is]...>{};
}(c, std::make_index_sequence<3>{});
}
int main()
{
constexpr auto r = foo_from_container([]{ return container{42, 43, 44}; });
static_assert(r.t() == std::tuple{42, 43, 44});
}
live example on wandbox.org