Transpose a container of structs - c++

Let we have a struct Record{uint8_t x, y;};, a container Container<Record> of structs and a struct Transposed{Container<uint8_t> x,y};. Container c is a template which first arg is a type of value, all of the rest args have default values. For example it can be a std::vector (the rest args are types) or std::span (the rest arg is a value). The template should work with all of them. Also we may like to pass tne rest of template arguments to underlying template.
How we can get the Transposed from the container using templates?
I have tried variadic templates,
#include <iostream>
#include <vector>
template <typename value_type=uint8_t> struct Record{
value_type x, y;
};
template<typename value_type, typename value_type2=uint8_t> class ContainerA: public std::vector<value_type>{
value_type2 b=1u;
};
template<typename value_type, uint8_t int_value=1u> class ContainerB: public std::vector<value_type>{};
template<typename value_type, template <typename ...> typename container_type> class Transposed{
container_type<value_type> x, y;
public:
Transposed(container_type<Record<value_type>> & recs){
x.reserve(recs.size());
y.reserve(recs.size());
x.resize(recs.size());
y.resize(recs.size());
size_t i=0;
for(auto &rec :recs){
x[i] = rec.x;
y[i] = rec.y;
++i;
}
}
};
int main(){
std::vector<Record<uint8_t>> recsV{
{1, 2},
{3, 4}
};
Transposed trV{recsV};
std::cout<<"vec"<<std::endl;
ContainerA<Record<uint8_t>> recsA{
{1, 2},
{3, 4}
};
Transposed trA{recsA};
std::cout<<"A"<<std::endl;
/*ContainerB<Record<uint8_t>> recsB{
{1, 2},
{3, 4}
};
Transposed trB{recsB};
std::cout<<"B"<<std::endl;*/
return 0;
}
but it seems they cannot match both types and values. Usage of more than 1 variadic template argument is not allowed. Is it a flaw in C++ language or a deliberate design choice and do we need something like any_template_arg keyword or should just specifying 2 variadic arguments of different types be allowed to allow this use case?

As far I know, there isn't a way to match types and values (and template-template) template arguments together. And I've searched it for a long time.
So I don't see a way to make what do you want in a simple and elegant way.
Trying to respond to your question
How we can get the Transposed from the container using templates?
the best I can imagine is declare (isn't necessary define them taking in count are used only inside a decltype()) a couple of trivial functions as follows
template <typename VT,
template <typename...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);
template <typename VT,
template <typename, auto...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);
The first one is to remove the Record part when the CT container accepts a variadic list of types (std::vector and ContainerA cases); the second one is for CT containers accepting (after the type parameter) one or more values (ContainerB case).
Obviously this doen't cover all possible cases, but it's trivial declare other extract_func() functions to cover other cases.
Now you can declare Tranposed as follows
template <typename T,
typename CT = decltype(extract_func(std::declval<T>()))>
class Transposed
Observe that, now, Transposed accept a generic type T but is SFINAE enabled only when the T type matches (as argument) an extract_func() declaration.
In the Transposed body you can use CT to declare x and y and T for the argument of the constructor.
The following is a full compiling example
#include <iostream>
#include <vector>
template <typename value_type=std::uint8_t>
struct Record
{ value_type x, y; };
template <typename value_type, typename value_type2=std::uint8_t>
class ContainerA : public std::vector<value_type>
{ value_type2 b=1u; };
template <typename value_type, std::uint8_t int_value=1u>
class ContainerB : public std::vector<value_type>
{ };
template <typename VT,
template <typename...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);
template <typename VT,
template <typename, auto...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);
template <typename T,
typename CT = decltype(extract_func(std::declval<T>()))>
class Transposed
{
private:
CT x, y;
public:
Transposed (T & recs)
{
x.reserve(recs.size());
y.reserve(recs.size());
x.resize(recs.size());
y.resize(recs.size());
std::size_t i=0u;
for(auto &rec :recs){
x[i] = rec.x;
y[i] = rec.y;
++i;
}
}
};
int main ()
{
std::vector<Record<std::uint8_t>> recsV { {1, 2}, {3, 4} };
Transposed trV{recsV};
std::cout<<"vec"<<std::endl;
ContainerA<Record<std::uint8_t>> recsA { };
Transposed trA{recsA};
std::cout<<"A"<<std::endl;
ContainerB<Record<std::uint8_t>> recsB { };
Transposed trB{recsB};
std::cout<<"B"<<std::endl;
}

One working example for you:
template<typename value_type=uint8_t>
struct Record{
value_type x, y;
};
template<class T>
std::vector<T> rebind_container(std::vector<Record<T>> const&);
// Span transposes into vector.
template<class T>
std::vector<T> rebind_container(std::span<Record<T>> const&);
template<class T>
std::list<T> rebind_container(std::list<Record<T>> const&);
inline void reserve(...) {}
template<class... Args>
inline void reserve(std::vector<Args...>* v, size_t n) {
v->reserve(n);
}
template<class container_type>
struct Transposed {
using container_type2 = decltype(rebind_container(std::declval<container_type>()));
container_type2 x, y;
Transposed(container_type const& recs) {
auto const n = recs.size();
reserve(&x, n);
reserve(&y, n);
for(auto& rec : recs) {
x.push_back(rec.x);
y.push_back(rec.y);
}
}
};
int main(){
std::vector<Record<uint8_t>> recsV{
{1, 2},
{3, 4}
};
Transposed<decltype(recsV)> trV{recsV};
std::cout << trV.x.size() << std::endl;
std::list<Record<uint8_t>> recsV2{
{1, 2},
{3, 4}
};
Transposed<decltype(recsV2)> trV2{recsV2};
std::cout << trV2.x.size() << std::endl;
}

Related

How to have variadic templates with a type and a size?

Just for fun I am trying to overload a struct, one for a std::array<T,SIZE>, one for a std::vector<T>, and a std::unordered_map<T,U>. So I did the following:
template<typename... T>
struct Cont;
template<typename T, std::size_t SIZE>
struct Cont<T,SIZE>
{
Cont(std::string n) : name(n){}
std::string name;
std::array<T,SIZE> array;
};
template<typename T>
struct Cont<T>
{
Cont(std::string n) : name(n){}
std::string name;
std::vector<T> vector;
};
template<typename T, typename U>
struct Cont<T,U>
{
Cont(std::string n) : name(n){}
std::string name;
std::unordered_map<T,U> unordered_map;
};
However, when I try to compile it, I get the error expected a type, got SIZE. Which I totally understand is because typename... T is expecting a type and not a std::size_t. So I tried:
template<std::size_t SIZE, typename... T>
struct Cont;
template<typename... T>
struct Cont;
Which also doesn't work since I am then redefining and not overloading like I originally thought I was doing. I also realize, that I can do:
template<std::size_t SIZE, typename... T>
struct Cont;
However I am trying to keep this as clean as possible in the sense when I declare them I want to be able to do:
int main()
{
Cont<int,5> myArray("myArray");
myArray.array = {1,2,3,4,5};
Cont<int> myVector("myVector");
myVector.vector = {1,2,3,4};
Cont<int,int> myMap("myMap");
myMap.unordered_map[0] = 2;
}
Is there a way to either overload the template? (I assume this is no) or construct a template in a way that says I'm either typename... T or typename T, std::size_t SIZE?
I'm not sure how to get exactly what you want. Maybe a more clever user has an exact solution. But one alternative might be to just have a type parameter pack for your template and use std::integral_constants for your numeric template arguments. Then you could specialize for each case. For example :
#include <array>
#include <type_traits>
#include <unordered_map>
#include <vector>
template<class...>
struct Cont;
template<class T, class U, U S>
struct Cont<T, std::integral_constant<U, S>>
{
Cont(const char *) {};
std::array<T, S> array;
};
template<class T>
struct Cont<T>
{
Cont(const char *) {};
std::vector<T> vector;
};
template<class T>
struct Cont<T, T>
{
Cont(const char *) {};
std::unordered_map<T, T> unordered_map;
};
int main()
{
Cont<int,std::integral_constant<int, 5>> myArray("myArray");
myArray.array = {1,2,3,4,5};
Cont<int> myVector("myVector");
myVector.vector = {1,2,3,4};
Cont<int,int> myMap("myMap");
myMap.unordered_map[0] = 2;
}
You could make it be Cont<std::array<int, 5>>, and then have a template<typename T, std::size_t SIZE> struct Cont<std::array<T, SIZE>>; specialisation, but that breaks the pattern of the other types.
Another thing you can have is, in addition to making 5 into a type by wrapping it in a std::integral_constant, make a pair of overloaded helper functions to automatically do it for you:
template<typename... T>
Cont<T...> make_cont(std::string name) {
return { std::move(name) };
}
template<typename T, std::size_t SIZE>
Cont<T, std::integral_constant<std::size_t, SIZE>> make_cont(std::string name) {
return { std::move(name) };
}
int main() {
auto myArray = make_cont<int, 5>("myArray");
myArray.array = {1,2,3,4,5};
auto myVector = make_cont<int>("myVector");
myVector.vector = {1,2,3,4};
auto myMap = make_cont<int, int>("myMap");
myMap.unordered_map[0] = 2;
}
If you declare the array version like this
template <typename T, std::size_t SIZE>
struct Cont<std::array<T, SIZE>>
{
Cont(std::string n) : name(n) {}
std::string name;
std::array<T, SIZE> array;
};
Then in main you can use it like this:
int main() {
Cont<std::array<int, 5>> myArray("myArray");
myArray.array = {1, 2, 3, 4, 5};
Cont<int> myVector("myVector");
myVector.vector = {1, 2, 3, 4};
Cont<int, int> myMap("myMap");
myMap.unordered_map[0] = 2;
std::cout << "myArray " << myArray.array[0] << std::endl;
// myArray 1
std::cout << "myVector " << myVector.vector[0] << std::endl;
// myVector 1
std::cout << "myMap " << myMap.unordered_map[0] << std::endl;
// myMap 2
}
I think this is the functionality you are looking for and it's is clean and readable.
This works because std::array<int, 5> names a type that template <typename... T> is expecting and it contains the std::SIZE_T information you need to define a std::array.

array length deduction from argument

Given a function with the parameter signature std::array<const size_t, V> shape like:
template<int V>
struct Cls {int val(){return V;} };
template <int V>
auto make(std::array<const size_t, V> shape) -> Cls<V>{
return Cls<V>();
}
I need to always add the template parameter V like
auto t1 = make<2>({2, 3}); // does work but need the information twice
std::cout << t1.val() << std::endl;
as the braced-initializer list seems to be casted into a c++11 std::array. However, this seems to me redundant. The length of {2, 3} is two. I know it and the compiler should also know it. Is it possible to wrap it into:
// auto t0 = make({2, 3}); // doesn't work
// auto t0 = make(2, 3); // would be also ok
I tried something like determining the size of the std::array as a constexpr. But I can't get rid of the template argument <2>.
template <int V>
constexpr size_t arr_len(std::array<const size_t, V>){return V;}
template <typename V>
auto make2(V shape) -> Cls<arr_len(shape)>{
return Cls<arr_len(shape)>();
}
I add a runnable MWE here:
https://ideone.com/wrVe9M
This thread seems to be related but I do not see how it might help.
Any suggestions?
I suppose you can use a variadic make() function (make0() in the following example) that can calculate the size as sizeof...(Is)
The following is a full working example
#include <array>
#include <iostream>
template<int V>
struct Cls {int val(){return V;} };
template <int V>
auto make(std::array<const size_t, V> shape) -> Cls<V>{
return Cls<V>();
}
template <typename ... Is>
auto make0 (Is ... is) -> Cls<sizeof...(Is)>
{ return make<sizeof...(Is)>({ {std::size_t(is)... } }); }
int main ()
{
auto t1 = make0(2, 3, 5, 7);
std::cout << t1.val() << std::endl;
}

c++ use template to make a most generic function

I need to make a function that should be as generic as possible, suppose I have a couple of maps to work with --
int main()
{
map<int, string> m1 = {{0, "abc"}, {1, "def"}, {2, "ghi"}} ;
map<int, double> m2 = {{0, 0.5}, {1, 0.6}, {2, 0.7}} ;
map<int, vector<string>> m3 = {{0, {"abc", "def"}},
{1, {"ghi", "ijk"}}};
dosomething(m1);
dosomething(m2);
}
and my templated dosomething() function looks like this --
template <typename A, typename B>
void dosomething(map<A,B> m)
{
for(auto e : m)
{
// do something with e
}
}
Now if I want to write a function that works with any kind of map (i.e. map<int, vector<string> or map<int, string> or map<int, double>) how do I do that ? It would be really nice if I could do like something this --
template <typename A, typename B>
void dosomething(map<A,B> m)
{
for(auto me: m)
{
if (the type of B is not a standard container type)
{
// do something with me.second
}
else if(the type of B is a something from the standard container)
{
for(int i = 0 ; i < (me.second).size() ; i++)
// do something with me.second[i]
}
}
}
How do I do that in c++ ? I am assuming the compiler follows c++11 specs.
The code in template instantiations is meant to compile. As a result, when doing different things depending on the types of template arguments, you typically can't have them in one function. Instead, you'd delegate the processing to a suitably overloaded function which itself may be a template made conditionally applicable. For example, your dosomething() function could look like this:
// suitable declaration/definition of dosomething_apply() go here; see below
template <typename A, typename B>
void dosomething(std::map<A, B> const& m) {
for (auto&& me: m) {
dosomething_apply(me.second);
}
}
As an aside: do not use for (auto e: m) unless you have a very good reason why you need this form! In the majority of the cases it is a performance problem. Likewise, you shouldn't pass bigger objects by value but rather pass them using a suitable reference type.
The slightly tricky business is determining whether the argument to dosomething_apply() is a standard container or not: there is certainly no type trait which classifies all standard as such. It is possible to create a corresponding type trait, though. The next question is whether this is actually what you want to detect because your supposed layout using indices on the element implies that me.second has an index-based subscript operator which is true only for std::vector<...>, std::dequeue<...>, std::basic_string<...>, std::array<...>, and std::bitset<...> (not sure if this is the complete set of containers providing subscripts). There are other containers which do not subscript operations, e.g. std::list<...>, std::map<...>, std::unordered_map<...>, etc.
All of these containers (except std::bitset<...>) provide an iterator interface, though. It may be more reasonable to detect whether a type provide an iterator interface and use this interface in the implementation of dosomething_apply(). Since you seem to be set to use index-based subscripts, the example below deals with the set of class templates noted above, though.
The first step is creating a suitable traits class which detects the desired types asking for special handling. In many cases presence of suitable member functions or member types can be detected. Since you specifically asked for standard types, it is necessary to list the supported types, though, as member functions or member types could also be detected for non-standard classes. Here is an example traits class called is_subscripted:
template <typename T>
struct is_subscripted: std::false_type {};
template <typename T, std::size_t N>
struct is_subscripted<std::array<T, N>>: std::true_type {};
template <std::size_t N>
struct is_subscripted<std::bitset<N>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::deque<cT, Al>>: std::true_type {};
template <typename cT, typename Tr, typename Al>
struct is_subscripted<std::basic_string<cT, Tr, Al>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::vector<cT, Al>>: std::true_type {};
It simply creates a default version of trait which states that trait isn't matched by deriving from std::false_type. The corresponding template is then specialized for the class templates listed above, each one rather deriving from std::true_type. This way an expression of the form is_specialized<T>::value can be used to detect if the trait applies to the type T.
The next step is providing suitable handler functions. Since the trait is either present or absent, using enable_if_t<...> is a simple way to go:
template <typename T>
std::enable_if_t<!is_subscripted<T>::value>
dosomething_apply(T const& value) {
std::cout << "value=" << value << '\n';
}
template <typename T>
std::enable_if_t<is_subscripted<T>::value>
dosomething_apply(T const& range) {
for (auto size(range.size()), i(size - size); i != size; ++i) {
std::cout << "range=" << range[i] << '\n';
}
}
Using std::enable_if_t<Value> with a Value evaluating to true yields void (using a second argument, e.g., std::enable_if_t<Value, double> to get a different type) and the function template becomes applicable. Using it with a Value evaluating to false doesn't yield a type and the function template is ignored (see SFINAE for an explanation of what's going on).
... and that's it really. All it takes to put together a suitable program and things should work. Below is a complete program ready to be fed to a C++14 compiler. There are some minor uses of C++14 in the code above, e.g. using std::enable_if_v<T> instead of typename std::enable_if<T>::type. It should be relatively straight forward to replace the C++14 usage with C++11 usage if you need to pass the code through a compiler for the previous C++ standard.
#include <map>
#include <iostream>
#include <utility>
#include <array>
#include <bitset>
#include <deque>
#include <string>
#include <vector>
// ----------------------------------------------------------------------------
template <typename T>
struct is_subscripted: std::false_type {};
template <typename T, std::size_t N>
struct is_subscripted<std::array<T, N>>: std::true_type {};
template <std::size_t N>
struct is_subscripted<std::bitset<N>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::deque<cT, Al>>: std::true_type {};
template <typename cT, typename Tr, typename Al>
struct is_subscripted<std::basic_string<cT, Tr, Al>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::vector<cT, Al>>: std::true_type {};
// ----------------------------------------------------------------------------
template <typename T>
std::enable_if_t<!is_subscripted<T>::value>
dosomething_apply(T const& value) {
std::cout << "value=" << value << '\n';
}
template <typename T>
std::enable_if_t<is_subscripted<T>::value>
dosomething_apply(T const& range) {
for (auto size(range.size()), i(size - size); i != size; ++i) {
std::cout << "range=" << range[i] << '\n';
}
}
// ----------------------------------------------------------------------------
template <typename A, typename B>
void dosomething(std::map<A, B> const& m)
{
for (auto&& me: m) {
dosomething_apply(me.second);
}
}
int main()
{
dosomething(std::map<int, int>{ { 1, 2 }, {3, 4 } });
dosomething(std::map<int, std::array<int, 2> >{ { 1, { { 2, 3 }} }, {4, { { 5, 6 } } } });
dosomething(std::map<int, std::bitset<4> >{ { 1, std::bitset<4>("1010") }, {2, std::bitset<4>("0011") } });
dosomething(std::map<int, std::deque<int>>{ { 10, { { 12, 13, 14 } } }, { 20, { 22, 23, 24 } } });
dosomething(std::map<int, std::string>{ { 1, "one" }, {2, "two" } });
dosomething(std::map<int, std::vector<int>>{ { 30, { { 32, 33, 34 } } }, { 40, { 42, 43, 44 } } });
}

Implementing std::array-like constructors in other classes

In all the modern C++ compilers I've worked with, the following is legal:
std::array<float, 4> a = {1, 2, 3, 4};
I'm trying to make my own class that has similar construction semantics, but I'm running into an annoying problem. Consider the following attempt:
#include <array>
#include <cstddef>
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
public:
template<typename... Types>
float_vec(Types... args)
: underlying_array{{args...}}
{
}
};
int main()
{
float_vec<4> v = {1, 2, 3, 4}; // error here
}
When using int literals like above, the compiler complains it can't implicitly convert int to float. I think it works in the std::array example, though, because the values given are compile-time constants known to be within the domain of float. Here, on the other hand, the variadic template uses int for the parameter types and the conversion happens within the constructor's initializer list where the values aren't known at compile-time.
I don't want to do an explicit cast in the constructor since that would then allow for all numeric values even if they can't be represented by float.
The only way I can think of to get what I want is to somehow have a variable number of parameters, but of a specific type (in this case, I'd want float). I'm aware of std::initializer_list, but I'd like to be able to enforce the number of parameters at compile time as well.
Any ideas? Is what I want even possible with C++11? Anything new proposed for C++14 that will solve this?
A little trick is to use constructor inheritance. Just make your class derive from another class which has a pack of the parameters you want.
template <class T, std::size_t N, class Seq = repeat_types<N, T>>
struct _array_impl;
template <class T, std::size_t N, class... Seq>
struct _array_impl<T, N, type_sequence<Seq...>>
{
_array_impl(Seq... elements) : _data{elements...} {}
const T& operator[](std::size_t i) const { return _data[i]; }
T _data[N];
};
template <class T, std::size_t N>
struct array : _array_impl<T, N>
{
using _array_impl<T, N>::_array_impl;
};
int main() {
array<float, 4> a {1, 2, 3, 4};
for (int i = 0; i < 4; i++)
std::cout << a[i] << std::endl;
return 0;
}
Here is a sample implementation of the repeat_types utility. This sample uses logarithmic template recursion, which is a little less intuitive to implement than with linear recursion.
template <class... T>
struct type_sequence
{
static constexpr inline std::size_t size() noexcept { return sizeof...(T); }
};
template <class, class>
struct _concatenate_sequences_impl;
template <class... T, class... U>
struct _concatenate_sequences_impl<type_sequence<T...>, type_sequence<U...>>
{ using type = type_sequence<T..., U...>; };
template <class T, class U>
using concatenate_sequences = typename _concatenate_sequences_impl<T, U>::type;
template <std::size_t N, class T>
struct _repeat_sequence_impl
{ using type = concatenate_sequences<
typename _repeat_sequence_impl<N/2, T>::type,
typename _repeat_sequence_impl<N - N/2, T>::type>; };
template <class T>
struct _repeat_sequence_impl<1, T>
{ using type = T; };
template <class... T>
struct _repeat_sequence_impl<0, type_sequence<T...>>
{ using type = type_sequence<>; };
template <std::size_t N, class... T>
using repeat_types = typename _repeat_sequence_impl<N, type_sequence<T...>>::type;
First of what you are seeing is the default aggregate initialization. It has been around since the earliest K&R C. If your type is an aggregate, it supports aggregate initialization already. Also, your example will most likely compile, but the correct way to initialize it is std::array<int, 3> x ={{1, 2, 3}}; (note the double braces).
What has been added in C++11 is the initializer_list construct which requires a bit of compiler magic to be implemented.
So, what you can do now is add constructors and assignment operators that accept a value of std::initializer_list and this will offer the same syntax for your type.
Example:
#include <initializer_list>
struct X {
X(std::initializer_list<int>) {
// do stuff
}
};
int main()
{
X x = {1, 2, 3};
return 0;
}
Why does your current approach not work? Because in C++11 std::initializer_list::size is not a constexpr or part of the initializer_list type. You cannot use it as a template parameter.
A few possible hacks: make your type an aggregate.
#include <array>
template<std::size_t N>
struct X {
std::array<int, N> x;
};
int main()
{
X<3> x = {{{1, 2, 3}}}; // triple braces required
return 0;
}
Provide a make_* function to deduce the number of arguments:
#include <array>
template<std::size_t N>
struct X {
std::array<int, N> x;
};
template<typename... T>
auto make_X(T... args) -> X<sizeof...(T)>
// might want to find the common_type of the argument pack as well
{ return X<sizeof...(T)>{{{args...}}}; }
int main()
{
auto x = make_X(1, 2, 3);
return 0;
}
If you use several braces to initialize the instance, you can leverage list-init of another type to accept these conversions for compile-time constants. Here's a version that uses a raw array, so you only need parens + braces for construction:
#include <array>
#include <cstddef>
template<int... Is> struct seq {};
template<int N, int... Is> struct gen_seq : gen_seq<N-1, N-1, Is...> {};
template<int... Is> struct gen_seq<0, Is...> : seq<Is...> {};
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
template<int... Is>
constexpr float_vec(float const(&arr)[n], seq<Is...>)
: underlying_array{{arr[Is]...}}
{}
public:
constexpr float_vec(float const(&arr)[n])
: float_vec(arr, gen_seq<n>{})
{}
};
int main()
{
float_vec<4> v0 ({1, 2, 3, 4}); // fine
float_vec<4> v1 {{1, 2, 3, 4}}; // fine
float_vec<4> v2 = {{1, 2, 3, 4}}; // fine
}
Explicitly specify that the data type for the initialization to floating point type. You can do this by doing "1.0f" instead of putting "1". If it is a double, put "1.0d". If it is a long, put "1l" and for unsigned long put "1ul" and so on..
I've tested it here: http://coliru.stacked-crooked.com/a/396f5d418cbd3f14
and here: http://ideone.com/ZLiMhg
Your code was fine. You just initialized the class a bit incorrect.
#include <array>
#include <cstddef>
#include <iostream>
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
public:
template<typename... Types>
float_vec(Types... args)
: underlying_array{{args...}}
{
}
float get(int index) {return underlying_array[index];}
};
int main()
{
float_vec<4> v = {1.5f, 2.0f, 3.0f, 4.5f}; //works fine now..
for (int i = 0; i < 4; ++i)
std::cout<<v.get(i)<<" ";
}

A puzzle for template wizards

I'd like to do the following:
const int someInt;
const std::vector<int> someIntList;
const std::vector<std::vector<int>> someNestedIntList;
Marshall(someInt); // trivial case
Marshall(someIntList); // difficult case
Marshall(someNestedIntList); // difficult case
I tried the following:
template<std::vector<class Element>>
void Marshall(const std::vector<Element>& toBeMarshalled)
{
for (int i=0; i<toBeMarshalled.size(); ++i)
Marshall<Element>(toBeMarshalled[i]);
}
Regrettably, this doesn't compile, and I failed to find the right syntax for it.
Note that there has to be only a single template parameter, otherwise the marshalling of a nested list won't work.
Update: Thanks to FredOverflow's answer, I found what I was looking for. I forgot that all container classes in the standard library have a value_type typedef. This can be used as a workaround for my problem:
template <class Container>
void Marshall(const Container& toBeMarshalled)
{
for (UINT32 i=0; i<toBeMarshalled.size(); ++i)
Marshall<Container::value_type>(toBeMarshalled);
}
It is a bit of a patch, but I think it is good enough.
There are two things wrong with your template:
The template declaration is wrong. You only list the template arguments here, not the function argument types. Also, >> is parsed as shift operator.
std::vector has two template parameters. Although in your daily work you will rarely use the second, it's still there and should be listed, or your template will fail if anyone ever attempts to use it with a std::vector that doesn't use the default allocator.
This should work for all std::vector instances:
template< typename T >
void Marshall(const T& toBeMarshalled)
{
// ...
}
template< typename T, class A >
void Marshall(const std::vector<T,A>& toBeMarshalled)
{
for (typename std::vector<T,A>::size_type i=0; i<toBeMarshalled.size(); ++i)
Marshall(toBeMarshalled[i]);
}
The template declaration is wrong. Do:
template< class Element >
void marshall( std::vector< Element > const& v )
Cheers & hth.,
May I propose SFINAE and some boost metaprogramming?
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
template <class T>
struct has_begin_end
{
template <class U,
typename U::const_iterator (U::*)() const,
typename U::const_iterator (U::*)() const>
struct sfinae { };
template <class U>
static char test(sfinae<U, &U::begin, &U::end>*);
template <class>
static long test(...);
enum { value = (1 == sizeof test<T>(0)) };
typedef boost::integral_constant<bool, value> type;
};
template <class Value>
typename boost::disable_if<has_begin_end<Value>, void>::type
Marshall(const Value& value)
{
std::cout << value << ' ';
}
template <class Container>
typename boost::enable_if<has_begin_end<Container>, void>::type
Marshall(const Container& c)
{
std::for_each(c.begin(), c.end(), Marshall<typename Container::value_type>);
}
int main()
{
const int someInt = 42;
const std::vector<int> someIntList {2, 3, 5, 7};
const std::vector<std::vector<int>> someNestedIntList {{11, 13}, {17, 19}};
Marshall(someInt);
Marshall(someIntList);
Marshall(someNestedIntList);
}
The code you've pasted contains >> at the end of your template declaration. C++ compilers will interpret that not as two closing angle brackets, but as a single right-shift operator.
Try template<std::vector<class Element> >, with a space between the brackets.