Compile time code expansion using template parameters - c++

I fully expect this to not be a feature, but figured I may as well ask; is it possible to expand code at compile time using template parameters?
For example:
template <size I>
void foo()
{
...double... vec;
}
Where the ... Is replaced by std::vector< >, I times.
So foo<2>() would compile to:
void foo()
{
std::vector<std::vector<double>> vec;
}
I can't even imagine what the syntax for this would be, so I'm not hopeful.
It would be useful for something like an N dimensional binning class, which could also be implemented through recursion, but not so neatly imo.

Yes, you can. You can do it with class templates and specializations, like this:
template<std::size_t N>
struct MultiDim {
using underlying = typename MultiDim<N-1>::type;
using type = std::vector<underlying>;
};
template<>
struct MultiDim<1> {
using type = std::vector<double>;
};
template<std::size_t N>
using multi_dimensional = typename MultiDim<N>::type;
Hence, multi_dimensional<1> is vector<double>, and multi_dimensional<N> where N>1 is vector<multi_dimensional<N-1>>.

is it possible to expand code at compile time using template parameters?
Directly... I don't think so.
But I suppose you can implement a specific type traits as follows
template <typename T, std::size_t I>
struct bar
{ using type = std::vector<typename bar<T, I-1U>::type>; };
template <typename T>
struct bar<T, 0U>
{ using type = T; };
and use in foo() in this way
template <std::size_t I>
void foo ()
{
typename bar<double, I>::type vec;
}
If you want to be a little more generic, you can also pass std::vector as a template-template parameter; if you define bar as follows
template <template <typename...> class C, typename T, std::size_t I>
struct bar
{ using type = C<typename bar<C, T, I-1U>::type>; };
template <template <typename ...> class C, typename T>
struct bar<C, T, 0U>
{ using type = T; };
foo() become
template <std::size_t I>
void foo ()
{
typename bar<std::vector, double, I>::type vec;
}

Related

Selecting container type at compile time

I want to select one of the standard containers with one template parameter at compile time. Something like
template<typename T>
void foo()
{
using Container = std::conditional_t< std::is_same_v<T, int>,
std::vector, // T is int
std::set>; // any other T
Container<T> bar;
}
How to do this properly?
Solution with std::conditional_t might be OK (and it was fixed in other answer), but IMHO it is better here use something more primitive: simple old fashioned template specialization:
template<typename T>
void foo()
{
using Container = std::set<T>;
Container<T> bar;
someCode(bar);
}
template<>
void foo<int>()
{
using Container = std::vector<T>;
Container<T> bar;
someOtherCode(bar);
}
The simplest way seems to be
using Container = std::conditional_t< std::is_same_v<T, int>,
std::vector<T>, // T is int
std::set<T>>;
Container bar;
std::conditional_t allows you to select a type. There is no standard conditional template that would allow to select a template. You can write one if you want but this won't be convenient to use.
template <bool t, template <typename...> typename X, template <typename...> typename Y>
struct conditional_template;
template <template <typename...> typename X, template <typename...> typename Y>
struct conditional_template<true, X, Y>
{
template <typename... ts> using type = X<ts...>;
};
template <template <typename...> typename X, template <typename...> typename Y>
struct conditional_template<false, X, Y>
{
template <typename... ts> using type = Y<ts...>;
};
template <typename... ts>
using Container = conditional_template<true, std::list, std::vector>::type<ts...>;
Container<int> x;
It doesn't seem possible to define an analogue of std::conditional_t convenience alias.
Without knowing exactly what "properly" means in your question, what you probably want is the following:
template<typename T>
void foo()
{
using Container = std::conditional_t<
std::is_same<T, int>::value,
std::vector<T>, // T is int
std::set<T>>; // any other T
Container bar;
}
Your original code did not work, because
std::conditional_t needs types as 2nd and 3rd parameters, but you spcified class templates instead.
There is no std::is_same_v in C++14, it arrived in C++17. You need to use std::is_same<X,Y>::value instead.
Full code here.

Parameterize of tuple with repeated type

I want to declare template:
template <size_t N, class Type> my_tuple
{
using type = ... //something here
};
So that this my_typle<3, std::string>::type,for example, will be the same as this std::tuple<std::string, std::string, std::string>
Please, show, how it can be done in one line, maybe, using std::index_sequence or something from boost or whatever? Or maybe it can not be done just simple?
UPD
Please, note I do not need an std::array, I need to parametrize some variable template with predefined list of types. I have used std::tuple as an example here.
This is fun. Here's a "pure" meta-programming approach to expand the sequence:
template<typename T, typename Seq>
struct expander;
template<typename T, std::size_t... Is>
struct expander<T, std::index_sequence<Is...>> {
template<typename E, std::size_t>
using elem = E;
using type = std::tuple<elem<T, Is>...>;
};
template <size_t N, class Type>
struct my_tuple
{
using type = typename expander<Type, std::make_index_sequence<N>>::type;
};
I say "pure" ironically. It's more akin to the classic meta-programming tricks, nothing else.
Use decltype with return type of function returning tuple<string,string,...repeated N times>:
template<typename T, size_t ... Indices>
auto GetType(std::index_sequence<Indices...>) {
return std::make_tuple( (Indices,T{})... );
}
template <size_t N, class Type> class my_tuple
{
using type = decltype(GetType<Type>(std::make_index_sequence<N>())); //something here
};
template<class T>
struct Dummy;
int main(){
Dummy<my_tuple<3,std::string>::type> d;
error on d gives you tuple<string,string,string>, so it is your desired type.
You can
template <typename...>
struct cat_tuple_type;
template <typename... T1, typename... T2>
struct cat_tuple_type<std::tuple<T1...>, std::tuple<T2...>>
{
using type = std::tuple<T1..., T2...>;
};
template <size_t N, class Type> struct my_tuple
{
static_assert(N>0);
using type = typename cat_tuple_type<std::tuple<Type>, typename my_tuple<N-1, Type>::type>::type;
};
template <class Type> struct my_tuple <1, Type>
{
using type = std::tuple<Type>;
};
Then my_typle<3, std::string>::type gives the type std::tuple<std::string, std::string, std::string>.

template alias for function with varying number of arguments

I have a class template Foo which has several members, one of which is a function bar of type Bar:
template<std::size_t N>
class Foo
{
...
Bar<N> bar;
...
};
I would like Bar<2> to be a template alias for a function double (* )(double, double) (or possibly std::function<double(double, double)>). Similarly, I want Bar<3> to be a template alias for a function double (* )(double, double, double) (or possibly std::function<double(double, double, double)>). This means that N should specify the number of double arguments that the function bar takes.
The only way I managed to get anywhere close to this behaviour, is by using the template alias
template <std::size_t N>
using Bar = double (* )(std::array<double, N>& eval);
In this way, however, I can not call the function bar in its natural way bar(x,y,z).
Is it possible to get the behaviour that I want?
With extra layer, you might do:
template <typename T>
struct BarHelper;
template <std::size_t ... Is>
struct BarHelper<std::index_sequence<Is...>>
{
private:
template <std::size_t, typename T>
using always_t = T;
public:
using type = double (*) (always_t<Is, double>...);
};
template <std::size_t N>
using Bar = typename BarHelper<std::make_index_sequence<N>>::type;
std::index_sequence is C++14, but can be implemented in C++11.
An other option, without index_sequence:
template<class T>
struct add_double_arg;
template<class R,class...Args>
struct add_double_arg<R(Args...)>{
using type = R(Args...,double);
};
template<int N,template<class> class Transform,class Init>
struct repeat{
using type = typename Transform<
typename repeat<N-1,Transform,Init>::type
>::type;
};
template<template<class> class Transform,class Init>
struct repeat<0,Transform,Init>{
using type = Init;
};
template<int N>
using Bar = typename repeat<N,add_double_arg,double()>::type *;

std::tuple of std::shared_ptr of template parameter pack

I want to implement a class template that:
behaves like a function
it's input and output variables are all shared.
relatively easy to use.
As a result, I construct the following:
// all input/output variable's base class
class basic_logic_parameter;
// input/output variable, has theire value and iterators to functions that reference to this variable
template <typename FuncIterator, typename ValueType>
class logic_parameter
:public basic_logic_parameter
{
private:
std::list<FuncIterator> _refedFuncs;
ValueType _val;
public:
};
// all `function`'s base class
class basic_logic_function
{
public:
virtual ~basic_logic_function() = 0;
};
// the function, has input/output variable
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base
:public basic_logic_function
{
private:
std::shared_ptr<logic_parameter<FuncIterator, R>> _ret;
std::tuple<std::shared_ptr<logic_parameter<FuncIterator, Args>>...> _args;
public:
template <std::size_t N>
decltype(auto) arg()
{
return std::get<N>(_args);
}
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N> type;
};
template <std::size_t N>
using arg_type_t = arg_type<N>::type;
decltype(auto) ret()
{
return _ret;
}
};
I wish to use as these like:
// drawing need color and a pen
struct Color
{
};
struct Pen
{
};
struct Iter
{
};
class Drawer
:public logic_function_base<Iter, void(Color, Pen)>
{
public:
void draw()
{
arg_type_t<0> pColor; // wrong
}
}
My compiler can not pass this code through, why? I just want convert a template parameter pack to std::tuple of std::shared_ptr of them.
for example:
Given struct A, int, struct C, I want to have:
std::tuple<
std::shared_ptr<logic_parameter<A>>,
std::shared_ptr<logic_parameter<int>>,
std::shared_ptr<logic_parameter<C>>,
>
The problem (once the small errors are fixed1) is that you instantiate:
logic_function_base<Iter, void(Color, Pen)>
...meaning that FuncIterator is Iter and R is void(Color, Pen), so Args is emtpy <>, so decltype(_args) is an empty std::tuple<>, and your code fails to obtain the type of the 0th element of an empty tuple, which is legit.
What you want is partial specialization of logic_function_base:
template <typename F, typename T>
class logic_function_base;
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base<FuncIterator, R(Args...)>: public basic_logic_function {
};
1 Small mistakes in your current code:
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N, decltype(_args)> type; // Missing the tuple type
};
template <std::size_t N>
using arg_type_t = typename arg_type<N>::type; // Missing a typename
This may not answer your whole question, but you could use the following trait to wrap tuple element types.
template <typename T> struct wrap;
template <typename... T>
struct wrap<std::tuple<T...>> {
using type = std::tuple<std::shared_ptr<logic_parameter<T>>...>;
}
template <typename T>
using wrap_t = typename wrap<T>::type;
You can then use it like this:
std::tuple<int,double,char> t1;
wrap_t<decltype(t)> t2;
The type of t2 is std::tuple<std::shared_ptr<logic_parameter<int>>,std::shared_ptr<logic_parameter<double>>,std::shared_ptr<logic_parameter<char>>>.

Accessing template arguments from specialization

template<typename T, typename U>
struct A;
template<std::size_t I>
struct A<int, char[I]> {
using pointer = T*; // does not compile
};
int main() {
A<int, char[3]> a;
}
Is there any way to access the type T (= int) from inside the class template specialization A<int, char[I]>, without explicitly writing the type that is has in the specialization?
Something like this:
template<class T, class U, class=T, class=U>
struct A;
template<std::size_t I, class T, class U>
struct A<int, char[I], T, U> {
using pointer = T*;
};
works. If someone actually passes a type for the second T and U problems develop, but...
Another approach is:
template<class T, class U>
struct A;
// get args from an instance!
template<class A>
struct A_Args;
template<class T_, class U_>
struct A_Args<A<T_,U_>> {
using T = T_; using U = U_;
};
template<class A>
using A_T = typename A_Args<A>::T;
template<class A>
using A_U = typename A_Args<A>::U;
// reflect on our troubles:
template<std::size_t I>
struct A<int, char[I]> {
using pointer = A_T<A>*;
};
where we have a using alias that extracts args from a generic A which we use within the specialization.
This version can be made generic with an interface like:
template<std::size_t I, class Instance>
struct nth_template_arg;
template<std::size_t I, class Instance>
using nth_template_arg_t=typename nth_template_arg<I, Instance>::type;
with the note it will only work with templates that only take type arguments. (implementation left as an exercise. I'd probably use tuple_element for a first pass; using tuples has the downside that they are heavy types, and metaprogramming with heavy types sucks performance and sometimes causes other problems.)
Simply,
#include <type_traits>
template<class T, class U>
struct foo;
template<class T, std::size_t Index>
struct foo<T, char[Index]>
{
using type = T;
};
int
main()
{
static_assert(std::is_same<foo<int, char[3]>::type, int>::value, "");
}
You can try this:
template<typename T, typename U, std::size_t I>
struct A{
using pointer = T*;
};