Related
I'm trying to accomplish this with HLS, not with "normal" C++, so most libraries (STL, boost, etc.) won't work as they can't be synthesized (manual memory management is not allowed). I think this should be possible with template metaprogramming, but I'm a little stuck.
I want to create an array of shift registers, each with a variable depth. I have N inputs, and I want to create N shift registers, with depths 1 to N, where N is known at compile time. My shift register class basically looks like
template<int DEPTH>
class shift_register{
int registers[DEPTH];
...
};
I tried following this and adapting it: Programmatically create static arrays at compile time in C++ , however, the issue is with the last line. Each templated shift register is going to be a different type, and so can't be put together in an array. But I do need an array, as there wouldn't be a way to access each shift register.
Any help would be appreciated!
Just to clarify, my problem was the following: generate N shift_registers, templated from 1 to N, where N is a compile time constant.
For example, if I had N=4, I could easily write this as:
shift_register<1> sr1;
shift_register<2> sr2;
shift_register<3> sr3;
shift_register<4> sr4;
But this wouldn't be easy to change, if I wanted a different value for N in the future.
I ended up using the preprocessor and took the solution from here: How do I write a recursive for-loop "repeat" macro to generate C code with the CPP preprocessor?
I used the macros from that solution like this:
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define BODY(i) shift_register<i> CAT(sr,i)
REPEAT_ADD_ONE(BODY, N, 1);
And then something similar to that in order to access the shift registers, in a sort of array fashion.
This let me achieve the compile time generation that I was looking for, and get the array type access I needed.
Your question was somewhat difficult to understand but I'll do my best...
template <typename ... Args>
constexpr auto make_array(Args && ... pArgs)
{
using type = std::common_type_t<std::decay_t<Args>...>;
return std::array<type, sizeof...(Args)>{ (type)pArgs ... };
}
Then use it like this:
auto constexpr var_array_of_arrays = std::make_tuple
(
make_array(1, 2, 3, 3),
make_array(2, 3, 4),
make_array(1, 2, 3 ,4 ,3, 5)
);
To get the M'th element you access it like this, n has to actually be a compile-time constant:
std::get<M>(var_array_of_arrays);
To access the Nth element in the Mth array:
auto constexpr value = std::get<M>(var_array_of_arrays)[N]
An to improve the interface:
template <size_t M, size_t N, typename T >
constexpr decltype(auto) get_element(T && pInput)
{
return std::get<M>(std::forward<T>(pInput))[N];
}
Used like this:
auto constexpr element0_1 = get_element<0, 1>(var_array_of_arrays);
This will allow you to use an array of variable length arrays, or atleast something that behaves like that and is identical to that in memory.
A full example is here:
Online compiler
Whenever I hear "compile time number sequence" I think std::index_sequence
namespace detail {
template <typename>
struct shift_registers;
template <std::size_t ... Is> // 0, 1, ... N-1
struct shift_registers<std::index_sequence<Is...> > {
using type = std::tuple<shift_register<Is + 1>...>;
};
template <typename T>
using shift_registers_t = typename shift_registers<T>::type
}
template <std::size_t N>
using shift_registers = detail::shift_registers_t<std::make_index_sequence<N>>;
The Tutorial: Metafunctions and Higher-Order Metaprogramming section of the Boost MPL library documentation states that transform can be invoked like so
typename mpl::transform<D1,D2, mpl::minus<_1,_2> >::type
where the placeholders _1 and _2 signify that when the transform's BinaryOperation is invoked, its first and second arguments will be passed on to minus in the positions indicated by _1 and _2, respectively.
I've been reading this over and over again for almost a month and I still don't understand it.
What values exactly do the placeholders _1 and _2 have? D1 and D2? If so, why not write mpl::minus<D1,D2>? Also considering that the placeholders are defined as typedef arg<1> _1; and typedef arg<2> _2; and consequently the original expression in my mind amounts to
typename mpl::transform<D1,D2, mpl::minus<<arg<1>,<arg<2> > >::type
I'm sure I'm thinking about placeholders the wrong way. I'd appreciate some guidance here.
Indeed, you're thinking about placeholders the wrong way.
mpl::minus is a template in MPL's metalanguage that symbolically represents (or corresponds to) a certain high-level behavior, namely, subtraction. You're thinking of it as if it's a non-meta construct such as a function
int minus(int a, int b) { return a - b; }
but it's not. (The standard C++11 library does have something kind of like that, called std::minus<>, but that's not what mpl::minus does!)
mpl::minus represents the subtraction operation at a higher level of abstraction. Don't worry for another couple of paragraphs about how mpl::minus is implemented. Just think about what it represents, which is subtraction of two things.
Ah, but which two things? Well, mpl::minus lets you specify those things as template parameters. For example,
mpl::minus<mpl::int_<7>, mpl::int_<3>>
expands to a type whose member typedef type is the same as mpl::int_<4>.
Okay, but in the dimensional analysis example from Boost, they don't have just two things; they have sequences D1 and D2 of dimensions. (This is a very important point!) Subtracting sequences isn't the same thing as subtracting integers; consider
auto a = std::vector<int>{ 1, 0, 0 };
auto b = std::vector<int>{ 0, 1, 0 };
auto c = (a - b); // Won't compile!
Likewise, in the meta space,
using a = mpl::vector<mpl::int_<1>, mpl::int_<0>, mpl::int_<0>>;
using b = mpl::vector<mpl::int_<1>, mpl::int_<0>, mpl::int_<0>>;
using c = mpl::minus<a,b>; // Won't compile!
What we mean to say, in the first case, is
auto c = std::vector<int>{};
std::transform(a.begin(), a.end(), b.begin(), std::back_inserter(c), std::minus<>{});
and what we mean to say, in the second (meta) case, is
using c = mpl::transform<a, b, mpl::minus>::type; // caveat: we're not done yet
Notice that the C++11 std::transform takes pairs of iterators a.begin(), a.end() instead of just a; it takes b.begin() but not b.end() (a deficiency that is only now being corrected by the Committee); and it mutates c via an output iterator, rather than returning a completely new object, for efficiency. MPL's compile-time meta-version takes containers a and b directly and returns a new container c, i.e., it has value semantics, which IMHO is strictly easier to think about.
So, the above is all correct, EXCEPT for one tiny detail! mpl::transform is actually a very generic algorithm, which means that it expects you to spell out the details of the transformation. You said "mpl::minus", which means "subtract", okay, but subtract what from what? Subtract the elements of the first sequence from elements of the second? Subtract the second's elements from the first? Subtract 42 from the elements of the second sequence and toss out the first one entirely?
Well, we mean "subtract the second sequence's elements, element-wise, from the first's." Which we write as
using c = mpl::transform<a, b, mpl::minus<_1, _2>>::type;
We could equally well write
using c = mpl::transform<b, a, mpl::minus<_2, _1>>::type;
— it would mean the exact same thing.
This generic transform algorithm lets us write complicated transforms such as
// hide some irrelevant boilerplate behind an alias
template<typename... Ts>
using multiplies_t = mpl::multiplies<Ts...>::type;
// compute c = a^2 + 2ab + 1
using c = mpl::transform<a, b,
mpl::plus<multiplies_t< _1, _1 >, // a^2 ...
multiplies_t< mpl::int_<2>, _1, _2 >, // ... + 2ab ...
mpl::int_<1>> // ... + 1
>::type;
Here we can refer to the same element of sequence a three times, using the symbol _1, while _2 refers to the corresponding element of sequence b.
So, that's the point of the symbols _1 and _2 in the context of mpl::transform. But you're probably still wondering how they're implemented. Well, there's no magic here. They might as well be implemented as
template<int> struct _ {};
using _1 = _<1>;
using _2 = _<2>;
As long as they get unique, distinguishable entities in C++'s type system, that's all MPL really cares about.
But in fact they're actually implemented as typedefs for specializations of mpl::arg, which leads to a nifty trick. Since _1 is a synonym for mpl::arg<1>, we can say
_1::apply<A,B,C>::type is the same type as A
_2::apply<A,B,C>::type is the same type as B
...
and I would guess that mpl::transform is able to take advantage of that fact internally.
I'm wanting to incorporate a luabind into one of my projects. To do so I need to provide a function which behaves similar to call_function (see below). This function uses some template magic (courtesy of Boost) that I'd appreciate some help with. This is the first time I've really come across template metaprogramming (is that what it's called?) and so I'm a little lost. Here's a few snippets I'd appreciate help with.
#define LUABIND_TUPLE_PARAMS(z, n, data) const A##n *
#define LUABIND_OPERATOR_PARAMS(z, n, data) const A##n & a##n
I'm not really sure what this preprocessor bit is up to, I don't even know what it's called so searching is a little difficult. A is a template type. If I remember correctly #a would insert the literal text of a, but what do the multiple # do? After this preprocessor stuff comes this.
template<class Ret BOOST_PP_COMMA_IF(BOOST_PP_ITERATION()) BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(), class A)>
typename boost::mpl::if_<boost::is_void<Ret>
, luabind::detail::proxy_function_void_caller<boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> >
, luabind::detail::proxy_function_caller<Ret, boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> > >::type
call_function(lua_State* L, const char* name BOOST_PP_COMMA_IF(BOOST_PP_ITERATION()) BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_OPERATOR_PARAMS, _) )
{
typedef boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> tuple_t;
#if BOOST_PP_ITERATION() == 0
tuple_t args;
#else
tuple_t args(BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(), &a));
#endif
}
As you can see it makes heavy use of Boost. I've googled BOOST_PP_ITERATION but still can't really make out what it's doing. Could someone please explain to me, preferably in the context of this code, what the BOOST_PP stuff is doing, and how it manages to get the arguments into args.
My end goal is to define a call_function within my own code that will generate args which I can pass to an overload of call_function which I'll define. This means I can use the same calling convention, but can also apply some preprocessing before invoking luabind.
This question is quite specific in the way I've worded it, but I hope the concepts are general enough for it to be OK on here.
BOOST_PP_* ist not related to template metaprogramming, its a preprocessor library. Like the name says, it's working with preprocessor magic, doing some really braintwisting things to generate a bunch of similar templates. In your case, that would be the following:
//preprocessor iteration 0
template<class Ret>
typename boost::mpl::if_<boost::is_void<Ret>
, luabind::detail::proxy_function_void_caller<boost::tuples::tuple<> >
, luabind::detail::proxy_function_caller<Ret, boost::tuples::tuple<> > >::type
call_function(lua_State* L, const char* name )
{
typedef boost::tuples::tuple<> tuple_t;
tuple_t args;
}
//preprocessor iteration 1
template<class Ret , class A0>
typename boost::mpl::if_<boost::is_void<Ret>
, luabind::detail::proxy_function_void_caller<boost::tuples::tuple<const A0 *> >
, luabind::detail::proxy_function_caller<Ret, boost::tuples::tuple<const A0 *> > >::type
call_function(lua_State* L, const char* name , const A0 & a0 )
{
typedef boost::tuples::tuple<const A0 *> tuple_t;
tuple_t args(&a0);
}
and so on, up to some maximum defined elsewhere (e.g. A0, A1, A2, A3... A9 if the maximum is 10)
The ## is a token concatenation for the preprocessor, in this case concatenation A (or a) with whatever value n has (=> A0, A1, A2, ...). The whole code is in some preprocessing loop.
BOOST_PP_ITERATION() gives the current loop index (0, 1, 2...)
BOOST_PP_COMMA_IF(X) gives a comma, if the argument is not 0, e.g. the comma before "class A0" in iteration 1 in the template parameter list
BOOST_PP_ENUM(n,B,C) gives a comma separated list of B(?, N, C), where N runs from 0..(n-1), i.e. the macro B gets executed n times, so calling BOOST_PP_ENUM(3, LUABIND_TUPLE_PARAMS, _) gives const A0 *, const A1 *, const A2 *
BOOST_PP_ENUM_PARAMS(n, X) gives a comma separated list of X##n, e.g. &a0, &a1, &a2 for BOOST_PP_ENUM_PARAMS(3, &a)
Many of the use cases for that Preprocessor magic can be done with variadic templates these days, so if you are lucky you will not come across that stuff again ;) It's not easy to grasp at first sight, because preprocessing does not work like other known C++ features and has some limitations that one has to work around, making it even less easy to understand.
In a project, I have several classes which encapsulate, among other things, matrices implemented as static-arrays, e.g.:
struct StaticContainer
{
static const short e[2][4];
};
/// Initialize components of member-array (with external linkage).
const short StaticContainer::e[2][4] = {
{ -1, 0, 0, 1 },
{ 0, -1, 1, 0 }
};
I would like to implement a meta-function which provides the inverse-mapping, from a column in StaticContainer::e back to the second index (1-4 in this case). Ideally, something like this:
template< typename TContainer, short TeX, short TeY >
struct VectorToIndex
{
enum { value = ??? };
};
Finally, I would like to pass (if this is possible at all):
BOOST_STATIC_ASSERT( 0 == VectorToIndex< StaticContainer, -1, 0 >::value );
Is this possible at all? My initial attempts to recursively-search through the 'e'-matrix failed, because whenever I try to access (at compile-time) the entries within I get (GCC):
error: ‘StaticContainer::e’ cannot appear in a constant-expression
Shall I understand that the values in the matrix are not available at compile-time?
I would appreciate any comments. I am free to change the way the matrix is initialized/stored (so I was thinking of some compile-time registration mechanism). The only constraint is to get this inverse-mapping at compile-time.
Clarifications:
Each column in the e-matrix represents a spatial direction (in this case, 2D). The columns are guaranteed to be distinct.
I would expect the following results from the meta-function:
VectorToIndex< StaticContainer, -1, 0 > --> '0' at compile-time
VectorToIndex< StaticContainer, 0,-1 > --> '1' at compile-time
VectorToIndex< StaticContainer, 0, 1 > --> '2' at compile-time
VectorToIndex< StaticContainer, 1, 0 > --> '3' at compile-time
If this template is instantiated with an invalid combination of numbers (i.e. which is not a column in the matrix), I would like to produce a compilation-error.
The solution I currently have is a simple program which writes files with the necessary template-instantiations manually. This satisfies the requirements (results are correct and for invalid vectors there is a compile-time error - since the corresponding template-instantiation is missing). However, since I have many classes similar to 'StaticContainer' in my codebase (many of them with larger matrices), this process generates thousands of lines of code :(.
As promissed, here goes a solution. Rather than reinventing a whole metaprogramming library using variadic templates, I used this opportunity to try out boost mpl and it turns out to be pretty expressive. Using it, VectorToIndex would look like the following:
template<typename basis, typename axis>
struct VectorToIndex :
boost::mpl::if_<
boost::mpl::greater<boost::mpl::size<basis>, typename boost::mpl::find<basis, axis>::type::pos>,
typename boost::mpl::find<basis, axis>::type::pos,
boost::mpl::empty_base
>::type
{
};
If "axis" is present in "basis" then VectorToIndex<basis, axis>::value equals its index in the range [0, size-of-basis). If otherwise this is not true, than VectorToIndex<basis, axis>::value is not defined, thus accessing it may be used to produce compile-time errors or
selective instanciation through SFINAE.
To represent the axis one should use boost::mpl::vector, like shown below:
typedef boost::mpl::vector<boost::mpl::int_<1>, boost::mpl::int_<0>, boost::mpl::int_<0> > e1;
typedef boost::mpl::vector<boost::mpl::int_<0>, boost::mpl::int_<1>, boost::mpl::int_<0> > e2;
typedef boost::mpl::vector<boost::mpl::int_<0>, boost::mpl::int_<0>, boost::mpl::int_<1> > e3;
Considering the definitions above, one has
VectorToIndex<boost::mpl::vector<e1, e2, e3>, e1>::value -> 0
VectorToIndex<boost::mpl::vector<e1, e2, e3>, e2>::value -> 1
VectorToIndex<boost::mpl::vector<e1, e2, e3>, e3>::value -> 2
VectorToIndex<boost::mpl::vector<e1, e2>, e3>::value -> COMPILE-TIME ERROR
There is a mismatch in your declarations:
typename TContainer means that the template parameter is a type
StaticContainer::e is a value
It cannot match.
There are a few potential solutions I can think of, but the first question is obviously: why not build the reverse matrix manually and use assert to prove its correctness ? It is certainly simpler, and simple is beautiful.
Is it possible to create a template function that takes a variable number of arguments, for example, in this Vector< T, C > class constructor:
template < typename T, uint C >
Vector< T, C >::Vector( T, ... )
{
va_list arg_list;
va_start( arg_list, C );
for( uint i = 0; i < C; i++ ) {
m_data[ i ] = va_arg( arg_list, T );
}
va_end( arg_list );
}
This almost works, but if someone calls Vector< double, 3 >( 1, 1, 1 ), only the first argument has the correct value. I suspect that the first parameter is correct because it is cast to a double during the function call, and that the others are interpreted as ints and then the bits are stuffed into a double. Calling Vector< double, 3 >( 1.0, 1.0, 1.0 ) gives the desired results. Is there a preferred way to do something like this?
Alas, right now there's no good way to do this. Most of the Boost packages that need to do something similar use macro tricks to define things like this:
template < typename T >
Vector< T >::Vector( T )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2 )
{ ... }
template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2, C c3 )
{ ... }
The macros generate some set number (typically around 10) versions, and provide a mechanism to change the max number of parameters before expanding the construction.
Basically, its a real pain which is why C++0x is introducing variable-length template arguments and delegation methods that will let you do this cleanly (and safely). In the meantime you can either do it with macros, or try a C++ compiler that has support for (some of) these new experimental features. GCC is a good one for this.
Be warned though that since C++0x isn't actually out yet, things can still change and your code may not be in sync with the final version of the standard. Plus, even after the standard comes out, there'll be 5 years or so during which many compilers will only partially support the standard, so your code won't be very portable.
This code looks dangerous and I think your analysis on why it isn't working is spot on, there's no way for the compiler to know that when calling:
Vector< double, 3 >( 1, 1, 1 )
the ones should be passed as doubles.
I would change the constructor to something like:
Vector< T, C >::Vector(const T(&data)[C])
instead, and have the user pass the arguments as an array. Another sort of ugly solution would be something like this:
template < typename T, uint C >
Vector< T, C >::Vector(const Vector<T, C - 1>& elements, T extra) {
}
and call it like this (with some typedefs):
Vector3(Vector2(Vector1(1), 1), 1);
You can do what you want, but don't do it, because it's not typesafe. Best pass a vector of T or a pair of iterators containing those values.
template < typename T, uint C >
Vector< T, C >::Vector(int N, ... )
{
assert(N < C && "Overflow!");
va_list arg_list;
va_start(arg_list, N);
for(uint i = 0; i < N; i++) {
m_data[i] = va_arg(arg_list, T);
}
va_end(arg_list);
}
Vector<int> v(3, 1, 2, 3);
This can be better solved, since all the elements are homogeneous typed anyway.
template < typename Iter, uint C >
Vector< T, C >::Vector(Iter begin, Iter end)
{
T *data = m_data;
while(begin != end)
*data++ = *begin++;
}
int values[] = { 1, 2, 3 };
Vector<int> v(values, values + 3);
Of course, you have to make sure there is enough place in m_data.
Does TypeList fit your needs?
Generic Programming:Typelists and Applications
Loki C++ Library
Tiny Template Library: implementing typelist
In C++0x (really should be called C++1x), you can use template varargs to achieve what you want in a typesafe fashion (and you won't even need to specify the number of arguments!). However, in the current version of C++ (ISO C++ 1998 with 2003 amendments), there is no way to accomplish what you want. You can either hold off or do what Boost does, which is use preprocessor macro magic to repeat the definition of the constructor multiple times with different numbers of parameters up to a hard-coded, but large limit. Given that Boost.Preprocessor is kind of complicating, you could just define all of the following yourself:
Vector<T,C>::Vector();
Vector<T,C>::Vector(const T&);
Vector<T,C>::Vector(const T&, const T&);
// ...
Since the above is kind of painful to do by hand, though, you could write a script to generate it.
std::tr1::array (which looks similar to yours) does not define a constructor, and can be initialized as an aggregate (?)
std::tr1::array<int, 10> arr = {{ 1, 2, 3, 4, 5, 6 }};
Also you could check out Boost.Assignment library.
For example the constructor could be
template < typename T, uint C >
template < typename Range >
Vector< T, C >::Vector( const Range& r )
and instances created with
Vector<int, 4> vec(boost::assign::cref_list_of<4>(1)(3)(4)(7));
You can use variadic , variadic means template with variable argument.more
The problem with variable arguments in constructors is :
you need the cdecl calling convention (or another one that can handle varargs)
you cant define cdecl for a constructor (in MSVS)
So the "correct" code (MS) could be :
template < typename T, uint C > __cdecl Vector< T, C >::Vector( T, ... )
but the compiler will say:
illegal calling convention for constructor/destructor (MS C4166)