If all of the members of std::tuple are of standard layout types, is that std::tuple itself standard layout? The presence of a user-defined copy-constructor makes it non-trivial, but I was wondering if it can still be standard layout.
A quote from the spec would be good.
No, standard layout requires that all nonstatic data members belong to either one base subobject or directly to the most derived type, and typical implementations of std::tuple implement one member per base class.
Because a member-declaration cannot be a pack expansion, in light of the above requirement, a standard layout tuple cannot have more than one member. An implementation could still sidestep the issue by storing all the tuple "members" inside one char[], and obtaining the object references by reinterpret_cast. A metaprogram would have to generate the class layout. Special member functions would have to be reimplemented. It would be quite a pain.
Inspired by PotatoSwatter's answer, I've dedicated my day to creating a standard layout tuple for C++14.
The code actually works, but is not currently suited for use as it involves undefined behaviour. Treat it as a proof-of-concept.
Here's the code I ended up with:
#include <iostream>
#include <type_traits>
#include <array>
#include <utility>
#include <tuple>
//get_size
template <typename T_head>
constexpr size_t get_size()
{
return sizeof(T_head);
}
template <typename T_head, typename T_second, typename... T_tail>
constexpr size_t get_size()
{
return get_size<T_head>() + get_size<T_second, T_tail...>();
}
//concat
template<size_t N1, size_t... I1, size_t N2, size_t... I2>
constexpr std::array<size_t, N1+N2> concat(const std::array<size_t, N1>& a1, const std::array<size_t, N2>& a2, std::index_sequence<I1...>, std::index_sequence<I2...>)
{
return { a1[I1]..., a2[I2]... };
}
template<size_t N1, size_t N2>
constexpr std::array<size_t, N1+N2> concat(const std::array<size_t, N1>& a1, const std::array<size_t, N2>& a2)
{
return concat(a1, a2, std::make_index_sequence<N1>{}, std::make_index_sequence<N2>{});
}
//make_index_array
template<size_t T_offset, typename T_head>
constexpr std::array<size_t, 1> make_index_array()
{
return {T_offset};
}
template<size_t T_offset, typename T_head, typename T_Second, typename... T_tail>
constexpr std::array<size_t, (sizeof...(T_tail) + 2)> make_index_array()
{
return concat(
make_index_array<T_offset, T_head>(),
make_index_array<T_offset + sizeof(T_head),T_Second, T_tail...>()
);
}
template<typename... T_args>
constexpr std::array<size_t, (sizeof...(T_args))> make_index_array()
{
return make_index_array<0, T_args...>();
}
template<int N, typename... Ts>
using T_param = typename std::tuple_element<N, std::tuple<Ts...>>::type;
template <typename... T_args>
struct standard_layout_tuple
{
static constexpr std::array<size_t, sizeof...(T_args)> index_array = make_index_array<T_args...>();
char storage[get_size<T_args...>()];
//Initialization
template<size_t T_index, typename T_val>
void initialize(T_val&& val)
{
void* place = &this->storage[index_array[T_index]];
new(place) T_val(std::forward<T_val>(val));
}
template<size_t T_index, typename T_val, typename T_val2, typename... T_vals_rest>
void initialize(T_val&& val, T_val2&& val2, T_vals_rest&&... vals_rest)
{
initialize<T_index, T_val>(std::forward<T_val>(val));
initialize<T_index+1, T_val2, T_vals_rest...>(std::forward<T_val2>(val2), std::forward<T_vals_rest>(vals_rest)...);
}
void initialize(T_args&&... args)
{
initialize<0, T_args...>(std::forward<T_args>(args)...);
}
standard_layout_tuple(T_args&&... args)
{
initialize(std::forward<T_args>(args)...);
}
//Destruction
template<size_t T_index, typename T_val>
void destroy()
{
T_val* place = reinterpret_cast<T_val*>(&this->storage[index_array[T_index]]);
place->~T_val();
}
template<size_t T_index, typename T_val, typename T_val2, typename... T_vals_rest>
void destroy()
{
destroy<T_index, T_val>();
destroy<T_index+1, T_val2, T_vals_rest...>();
}
void destroy()
{
destroy<0, T_args...>();
}
~standard_layout_tuple()
{
destroy();
}
template<size_t T_index>
void set(T_param<T_index, T_args...>&& data)
{
T_param<T_index, T_args...>* ptr = reinterpret_cast<T_param<T_index, T_args...>*>(&this->storage[index_array[T_index]]);
*ptr = std::forward<T_param<T_index, T_args...>>(data);
}
template<size_t T_index>
T_param<T_index, T_args...>& get()
{
return *reinterpret_cast<T_param<T_index, T_args...>*>(&this->storage[index_array[T_index]]);
}
};
int main() {
standard_layout_tuple<float, double, int, double> sltuple{5.5f, 3.4, 7, 1.22};
sltuple.set<2>(47);
std::cout << sltuple.get<0>() << std::endl;
std::cout << sltuple.get<1>() << std::endl;
std::cout << sltuple.get<2>() << std::endl;
std::cout << sltuple.get<3>() << std::endl;
std::cout << "is standard layout:" << std::endl;
std::cout << std::boolalpha << std::is_standard_layout<standard_layout_tuple<float, double, int, double>>::value << std::endl;
return 0;
}
Live example: https://ideone.com/4LEnSS
There's a few things I'm not happy with:
Alignment is not handled properly which means entering misaligned types will currently give you undefined behaviour
Not all tuple functionality is represented yet.
I don't think the memory management is currently exception-safe.
It uses tuple to determine the type for each index.
The overall code quality is kinda messy.
There might be better, more concise ways to handle some of the recursive template functions.
I don't fully understand everything I did. I understand the main basics, but I'm using some of the weirder syntax on good faith. Here's the most important sources I used:
Create N-element constexpr array in C++11
Lookup table with constexpr
c++11 constexpr flatten list of std::array into array
constexpr, arrays and initialization
template parameter packs access Nth type and Nth element
This is not yet suitable for use as-is, really only treat it as a proof-of-concept in this state. I will probably come back to improve on some of these issues. Or, if anyone else can improve it, feel free to edit.
One reason std::tuple cannot be of standard layout, as any classes with members and base classes with members, is that the standard allows for space optimization when deriving even non-empty base classes. For example:
#include <cstdio>
#include <cstdint>
class X
{
uint64_t a;
uint32_t b;
};
class Y
{
uint16_t c;
};
class XY : public X, public Y
{
uint16_t d;
};
int main() {
printf("sizeof(X) is %zu\n", sizeof(X));
printf("sizeof(Y) is %zu\n", sizeof(Y));
printf("sizeof(XY) is %zu\n", sizeof(XY));
}
Outputs:
sizeof(X) is 16
sizeof(Y) is 2
sizeof(XY) is 16
The above shows that the standard allows for class trailing padding to be used for the derived class members. Class XY has two extra uint16_t members, yet its size equals to the size of base class X.
In other words, class XY layout is the same as that of another class that has no base classes and all the members of XY ordered by address, e.g. struct XY2 { uint64_t a; uint32_t b; uint16_t c; uint16_t d; };.
What makes it non-standard layout is that the size of a derived class is not a function of sizes of base classes and derived class members.
Note that the size of a struct/class is a multiple of the alignment of one of its members with the largest alignment requirement. So that an array of objects is suitably aligned for such a member. For built-in types normally sizeof(T) == alignof(T). Hence sizeof(X) is a multiple of sizeof(uint64_t).
I am not sure whether the standard requires special treatment for struct, but with g++-5.1.1 if class is replaced with struct the above code yields different output:
sizeof(X) is 16
sizeof(Y) is 2
sizeof(XY) is 24
In other words, the trailing padding space optimization is not used when struct is involved (did not test for exact conditions).
The "list" approach can be used to get standard layout tuple (the following example has some inaccuracies but demonstrates the idea):
template <class... Rest>
struct tuple;
template <class T, class... Rest>
struct tuple<T, Rest...> {
T value;
tuple<Rest...> next;
};
template <>
struct tuple<> {};
namespace details {
template <size_t N>
struct get_impl {
template <class... Args>
constexpr static auto process(const tuple<Args...>& t) {
return get_impl<N - 1>::process(t.next);
}
};
template <>
struct get_impl<0> {
template <class... Args>
constexpr static auto process(const tuple<Args...>& t) {
return t.value;
}
};
}
template <size_t N, class... Args>
constexpr auto get(const tuple<Args...>& t) {
return details::get_impl<N>::process(t);
}
template <class... Args>
constexpr auto make_tuple(Args&&... args) {
return tuple<Args...>{std::forward<Args>(args)...};
}
Related
Problem:
Consider the following type:
struct S {
std::uint8_t a;
std::uint32_t b;
};
Is it possible to determine sizeof(S) from std::uint8_t and std::uint32_t solely?
Context:
I'm developing a code where S is actually a template type more verbose than this illustration-purpose one, so I thought in defining a traits class for generically determining the size of a type from the types of its data members. So far, I had two ideas:
Using alignment of data members
template <typename... Ts>
inline constexpr std::size_t aligned_sizeof_all_v = sizeof...(Ts) * std::max(alignof(Ts)...);
Using std::tuple
template <typename... Ts>
inline constexpr std::size_t aligned_sizeof_all_v = sizeof(std::tuple<Ts...>);
Using both approaches seem to succeed, although I'm reluctant about the second one since the implementation of std::tuple provides no guarantees with respect to its size:
static_assert(aligned_sizeof_all_v<std::uint8_t, std::uint32_t> == sizeof(S));
Is this a correct way of determining a type's size?
Yes. If the types and order of the object's members are known, and if all members have the same access specifier (e.g. public), and if the object lacks a vtable, you can rebuild the object's structure within a helper class and use the size of that:
template<typename... Ts>
struct struct_traits {
static constexpr std::size_t size() {
return sizeof(struct_builder<Ts...>);
}
private:
template<typename First, typename... Rest>
struct struct_builder : struct_builder<Rest...> {
First v;
};
template<typename First>
struct struct_builder<First> {
First v;
};
// Below: addition to work with structures declared with alignas().
public:
template<std::size_t alignas_>
static constexpr std::size_t size() {
return sizeof(aligned_struct_builder<alignas_, Ts...>);
}
private:
template<std::size_t alignas_, typename First, typename... Rest>
struct alignas(alignas_) aligned_struct_builder : aligned_struct_builder<alignas_, Rest...> {
First v;
};
template<std::size_t alignas_, typename First>
struct alignas(alignas_) aligned_struct_builder<alignas_, First> {
First v;
};
};
Example of use (try it on Godbolt):
struct S
{
uint8_t a, b;
uint32_t c;
};
static_assert(struct_traits<uint8_t, uint8_t, uint32_t>::size() == sizeof(S)); // True! 8 == 8
static_assert(struct_traits<uint8_t, uint32_t, uint8_t>::size() == sizeof(S)); // Bad: 12 != 8
Use against alignas-ed structure (on Godbolt):
struct alignas(8) S
{
uint8_t a;
uint32_t c;
uint8_t b;
};
static_assert(struct_traits<uint8_t, uint32_t, uint8_t>::size<8>(), sizeof(S)); // True: 16 == 16
Inspiration from this SO post.
If I have a class template which contains an array with another class as type with undefined amount of fields (the amount is a template parameter), how do I run their constructors (if they take parameters)?
Here some example code:
class ArrayClass
{
public:
ArrayClass() = delete;
ArrayClass(int anyParameter) {}
};
template <const int amountOfFields>
class ContainingClass
{
ArrayClass myArray[amountOfFields];
public:
ContainingClass();
};
template <const int amountOfFields>
ContainingClass<amountOfFields>::ContainingClass()
:
myArray(5) // doesn't work of cause
{}
Is it possible to give every ArrayClass, no matter how many there are, the same parameter (or different ones)? (I don't essentially need it but it would make things easier for me)
There’s nothing in the C++ standard libraries for this case
If you’re compiling with GCC, it has a proprietary extension called ranged initialization. With GCC, you can write something like this (untested):
template<size_t amountOfFields>
ContainingClass<amountOfFields>::ContainingClass():
myArray( { [0 ... (amountOfFields-1)] = 5} )
{ }
If you’re using any other compiler, you have following options.
As said by the commenters, replace array with std::vector, it has the constructor you need. However this will change RAM layout, i.e. if you have lots of containers with small number of elements each, arrays (both C arrays, and C++ std::array) will be faster because one less pointer to chase.
Remove “=delete” from the default constructor of your ArrayClass, use std::fill or std::fill_n in the ContainingClass constructor to set initial values after they’re already constructed. However this might bring some small runtime cost.
If you don’t have too many elements, technically you can use some template metaprogramming to implement statically-constructed arrays the way you want. However, IMO that’ll be substantial amount of very hard to debug C++ code (there’s no compile-time debugger).
If you have small number of different template arguments in your code, you can write a function like
template<size_t N>
constexpr std::array<ArrayClass,N> fill_array(int val)
specialize it for different values of amountOfFields temple arguments you have, and call the function in the constructor of ContainingClass.
Other solutions are possible, like external tools, macros, boost, etc… But I think 2 & 4 are the most reasonable workarounds.
This work for me with GCC 8.1 / Clang 6.0 and C++14, though I am definitely not sure whether it is Standard compliant:
class E {
public:
E() = delete;
E(int i) : i_(i) { }
operator int() const { return i_; }
private:
int i_;
};
template <typename T>
T dummy(T val, /* [[maybe_unused]] */ size_t I) { return val; }
template <typename T, size_t... I, typename U>
constexpr auto make_array_impl(U val, std::index_sequence<I...> is) {
return std::array<T, is.size()>{dummy(val, I)...};
}
template <typename T, size_t N, typename U>
constexpr auto make_array(U val) {
return make_array_impl<T>(val, std::make_index_sequence<N>{});
}
template <typename T, size_t N>
class A {
public:
A(T val) : a_{make_array<T, N>(val)} { }
void print() { for (auto e : a_) std::cout << e << std::endl; }
private:
std::array<T, N> a_;
};
int main() {
A<E, 5> a(-1);
a.print();
}
Live demo: https://wandbox.org/permlink/Db9Zpf6gUMvg4MER
Updated more generic solution:
template <typename T, size_t... I, typename... Args>
constexpr auto make_array_impl(std::index_sequence<I...> is, Args&&... args) {
return std::array<T, is.size()>{ (I, T(std::forward<Args>(args)...))... };
}
template <typename T, size_t N, typename... Args>
constexpr auto make_array(Args&&... args) {
return make_array_impl<T>(std::make_index_sequence<N>{}, std::forward<Args>(args)...);
}
I'm looking for a better way to calculate the sum of numeric template parameters associated with nested template classes. I have a working solution here, but I want to do this without having to create this extra helper template class DepthCalculator and partial specialization DepthCalculator<double,N>:
#include <array>
#include <iostream>
template<typename T,size_t N>
struct DepthCalculator
{
static constexpr size_t Calculate()
{
return N + T::Depth();
}
};
template<size_t N>
struct DepthCalculator<double,N>
{
static constexpr size_t Calculate()
{
return N;
}
};
template<typename T,size_t N>
class A
{
std::array<T,N> arr;
public:
static constexpr size_t Depth()
{
return DepthCalculator<T,N>::Calculate();
}
// ...
// Too many methods in A to write a separate specialization for.
};
int main()
{
using U = A<A<A<double,3>,4>,5>;
U x;
constexpr size_t Depth = U::Depth(); // 3 + 4 + 5 = 12
std::cout << "Depth is " << Depth << std::endl;
A<double,Depth> y;
// Do stuff with x and y
return 0;
}
The static function A::Depth() returns the proper depth at compile time, which can then be used as a parameter to create other instances of A. It just seems like a messy hack to have to create both the DepthCalculator template and a specialization just for this purpose.
I know I can also create a specialization of A itself with a different definition of Depth(), but this is even more messy due to the number of methods in A, most of which depend on the template parameters. Another alternative is to inherit from A and then specialize the child classes, but this also seems overly complicated for something that seems should be simpler.
Are there any cleaner solutions using C++11?
Summary Edit
In the end, this is the solution I went with in my working project:
#include <array>
#include <iostream>
template<typename T,size_t N>
class A
{
std::array<T,N> arr;
template<typename U>
struct Get { };
template<size_t M>
struct Get<A<double,M>> { static constexpr size_t Depth() { return M; } };
template<typename U,size_t M>
struct Get<A<U,M>>
{ static constexpr size_t Depth() { return M + Get<U>::Depth(); } };
public:
static constexpr size_t GetDepth()
{
return Get<A<T,N>>::Depth();
}
// ...
// Too many methods in A to write a separate specialization for.
};
int main()
{
using U = A<A<A<double,3>,4>,5>;
U x;
constexpr size_t Depth = U::GetDepth(); // 3 + 4 + 5 = 12
std::cout << "Depth is " << Depth << std::endl;
A<double,Depth> y;
// Do stuff with x and y
return 0;
}
Nir Friedman made some good points about why GetDepth() should be an external function, however in this case there are other Get functions (not shown) which are appropriately member functions, and therefore it would make the most sense to have GetDepth() a member function too. I also borrowed Nir's idea of having the Depth() functions only call themselves, rather than GetDepth() which creates a bit less circular dependencies.
I chose skypjack's answer because it most directly provided what I had originally asked for.
You said:
I want to do this without having to create this extra helper template class DepthCalculator
So, maybe this one (minimal, working example) is fine for you:
#include<type_traits>
#include<cassert>
template<class T, std::size_t N>
struct S {
template<class U, std::size_t M>
static constexpr
typename std::enable_if<not std::is_arithmetic<U>::value, std::size_t>::type
calc() {
return M+U::calc();
}
template<typename U, std::size_t M>
static constexpr
typename std::enable_if<std::is_arithmetic<U>::value, std::size_t>::type
calc() {
return M;
}
static constexpr std::size_t calc() {
return calc<T, N>();
}
};
int main() {
using U = S<S<S<double,3>,4>,5>;
static_assert(U::calc() == 12, "oops");
constexpr std::size_t d = U::calc();
assert(d == 12);
}
I'm not sure I got exactly your problem.
Hoping this can help.
If you are with C++14, you can use also:
template<class U, std::size_t M>
static constexpr
std::enable_if_t<not std::is_arithmetic<U>::value, std::size_t>
If you are with C++17, it becomes:
template<class U, std::size_t M>
static constexpr
std::enable_if_t<not std::is_arithmetic_v<U>, std::size_t>
The same applies to the other sfinaed return type.
Option #1
Redefine your trait as follows:
#include <array>
#include <cstddef>
template <typename T>
struct DepthCalculator
{
static constexpr std::size_t Calculate()
{
return 0;
}
};
template <template <typename, std::size_t> class C, typename T, std::size_t N>
struct DepthCalculator<C<T,N>>
{
static constexpr size_t Calculate()
{
return N + DepthCalculator<T>::Calculate();
}
};
template <typename T, std::size_t N>
class A
{
public:
static constexpr size_t Depth()
{
return DepthCalculator<A>::Calculate();
}
private:
std::array<T,N> arr;
};
DEMO
Option #2
Change the trait into function overloads:
#include <array>
#include <cstddef>
namespace DepthCalculator
{
template <typename T> struct tag {};
template <template <typename, std::size_t> class C, typename T, std::size_t N>
static constexpr size_t Compute(tag<C<T,N>>)
{
return N + Compute(tag<T>{});
}
template <typename T>
static constexpr size_t Compute(tag<T>)
{
return 0;
}
}
template <typename T, std::size_t N>
class A
{
public:
static constexpr std::size_t Depth()
{
return Compute(DepthCalculator::tag<A>{});
}
private:
std::array<T,N> arr;
};
DEMO 2
You can do this wholly non-intrusively, which I think is advantageous:
template <class T>
struct Depth
{
constexpr static std::size_t Calculate()
{
return 0;
}
};
template <class T, std::size_t N>
struct Depth<A<T, N>>
{
constexpr static std::size_t Calculate()
{
return N + Depth<T>::Calculate();
}
};
Usage:
using U = A<A<A<double,3>,4>,5>;
constexpr size_t depth = Depth<U>::Calculate(); // 3 + 4 + 5 = 12
I realize your original question was how to do this without the extra "helper template", which my solution still has. But on the flip side, it's moved the functionality completely out of A itself, so its not really a helper template any more, it's just a template. This is pretty short, doesn't have any template template parameters unlike Piotr's solutions, is easy to extend with other classes, etc.
I have a class vec_base defined like so:
template<typename T, std::size_t Size>
class vec_base;
and I would like to specialize it so that
vec_base<float, /* any multiple of 4 */>
and
vec_base<double, /* any multiple of 2 */>
can have specific members independently as apposed to, say
vec_base<int, 6>
which would have generic members that I have already defined
I'm having a tough time implementing this because of the lenient size allowed (any multiple of 4 or 2) if it were specifically 2 or 4 I know I could perform full specialization, but that isn't the case :/
How would I go about this? Any help at all is appreciated, I always love learning new language techniques!
EDIT
okay so I have this so far:
template<std::size_t Size>
struct is_div_by_4{static const bool value = (Size % 4 == 0);};
// have to define because of template requirements
// about not being dependent on other parameters
template<typename T, std::size_t Size, bool is_special>
class vec_base;
template<typename T, std::size_t Size>
class vec_base<T, Size, false>{
// generic implementation
};
teplate<std::size_t Size>
class vec_base<float, Size, is_div_by_4<Size>::value>{
// Special implementation (for sse, it's no secret)
};
but I haven't compiled it yet and I know it won't work, so please don't point that out; it's just what I have so far incase you thought I was just deferring my own work to SO.
Simple solution
The most simple technique would be using std::enable_if and std::same similar to what you did:
template<typename T, std::size_t Size, typename U = void>
class vec_base { /* implement me generically */ };
template<typename T, std::size_t Size>
class vec_base<T, Size, typename std::enable_if<std::is_same<T, float>::value && Size % 4 == 0>::type>
{ /* implement me with 10% more awesome-sauce */ };
template<typename T, std::size_t Size>
class vec_base<T, Size, typename std::enable_if<std::is_same<T, double>::value && Size % 2 == 0>::type>
{ /* implement me with pepper instead */ };
Why the typename U = void can be avoided
The idea behind std::enable if is something called the SFINAE principle, which basically states that whenever instantiating a template does not work, the compiler will not error out, but instead just remove that one definition from all overload sets and similar name resolutions.
The implementation behind std::enable_if specializes the class template, so that std::enable_if<false> does not contain a member type at all. Therefore using that type member will cause an error that (due to SFINAE) removes this specialization from consideration.
Since your template already contains a type parameter, you could instead use that type parameter, since the std::enable_if<true>::type is actually the same as its second parameter, a type parameter that only defaults to void, but can of course be set.
Therefore, you can remove the last template parameter in the generic implementation completely and instead specialize like so:
template<typename T, std::size_t Size>
class vec_base<typename std::enable_if<std::is_same<T, float>::value && Size % 4 == 0, float>::type, Size>
{ /* implement me with 10% more awesome-sauce */ };
Why the typename T and std::same are not necessary
From this you can also see that you could remove the typename T of your specializations and drop the usage of std::is_same. T must always be a specific type after all...
template<std::size_t Size>
class vec_base<typename std::enable_if<Size % 4 == 0, float>::type, Size>
{
friend vec_base operator+(vec_base const& lhs, vec_base const& rhs)
{ /* do some additions */
return lhs;
}
};
Adding more operators outside of the class is fairly simple:
// works for all vec_base variants
template<typename T, std::size_t Size>
vec_base<T, Size> operator-(vec_base<T, Size> const& lhs, vec_base<T, Size> const& rhs)
{ /* do some subtractions */
return lhs;
}
// works only for the specialization float, divisible by 4
template<std::size_t Size>
typename std::enable_if<Size % 4 == 0, vec_base<float, Size>>::type
operator-(vec_base<float, Size> const& lhs, vec_base<float, Size> const& rhs)
{ /* do some salty computations */
return lhs;
}
This actually works because the second version is strictly more restricted than the first version (every argument group that works with the special function also works for the generic one - but the generic one has some for which the special one will not work).
Fixing your attempt
Although you seem rather down about your attempt, here is how to adapt it to work as well (note that this solution is far more convoluted than the one shown above):
template<typename T, std::size_t Size, int mode =
(std::is_same<T, float>::value && Size % 4 == 0) ? 1
: (std::is_same<T, double>::value && Size % 2 == 0) ? 2
: 0>
struct vec_base;
template<typename T, std::size_t Size>
struct vec_base<T, Size, 0>
{ static void hello() { ::std::cout << "hello all\n"; } };
template<std::size_t Size>
struct vec_base<float, Size, 1>
{ static void hello() { ::std::cout << "hello 4 floats\n"; } };
template<std::size_t Size>
struct vec_base<double, Size, 2>
{ static void hello() { ::std::cout << "hello 2 doubles\n"; } };
You would call it like so:
vec_base<float, 2>::hello(); // hello all
vec_base<float, 4>::hello(); // hello 4 floats
Simpler yet:
template<typename T, std::size_t Size>
class vec_base
{
public: vec_base() {
std::cout << "I am generic, my size is " << Size << std::endl;
}
};
template<std::size_t Size>
class vec_base<typename std::enable_if<Size % 4 == 0, float>::type, Size>
{
public: vec_base() {
std::cout << "I am specialized for float, my size is " << Size << std::endl;
}
};
template<std::size_t Size>
class vec_base<typename std::enable_if<Size % 2 == 0, double>::type, Size>
{
public: vec_base() {
std::cout << "I am specialized for double, my size is " << Size << std::endl;
}
};
Encapsulate your specialness in a type trait
I would go for a is_special<T, N> type trait that gives std::false_type for all types that do not fit into your SSE solution, and std::true_type otherwise.
#include <cstddef>
#include <iostream>
#include <type_traits>
// test whether a vector of floating points fits into B bits SSE solution
template<typename T, std::size_t N, std::size_t B = 128>
struct is_special
:
std::integral_constant<bool,
std::is_floating_point<T>::value && 8 * sizeof(T) * N == B
>
{};
I generally don't like putting raw conditions in my template specializations that make use of such traits, as this can lead to less maintainable code (especially if you re-use the type trait anywhere else).
Specialize your template using enable_if
You can then do a std::enable_if on this type trait
template<typename, std::size_t, typename = void>
class vec_base
{
public:
static void print() { std::cout << "vec_base<T, N>\n"; };
};
template<std::size_t N>
class vec_base<float, N, typename std::enable_if<is_special<float, N>::value>::type>
{
public:
static void print() { std::cout << "vec_base<float, 4 * K>\n"; };
};
template<std::size_t N>
class vec_base<double, N, typename std::enable_if<is_special<double, N>::value>::type>
{
public:
static void print() { std::cout << "vec_base<double, 2 * K>\n"; };
};
Note that the generic template does not need to know about the kind of special conditions that you use for the specializations. This is a manifestion of the Open/Closed principle: your code is closed for modification, but open for extensions. For 128 bit long doubles, just add a new specialization.
Testing the code in practice
You can call this code like this:
int main()
{
vec_base<int, 6>::print(); // vec_base<T, N>
vec_base<float, 1>::print(); // vec_base<T, N>
vec_base<float, 4>::print(); // vec_base<float, 4 * K>
vec_base<double, 1>::print(); // vec_base<T, N>
vec_base<double, 2>::print(); // vec_base<double, 2 * K>
}
Live Example that prints the commented lines above. It's important to test both the cases you want and the cases you don't want to call SSE code.
Is it possible to iterate over all elements in a struct or class?
For example if I have a struct of three elements of different type:
struct A {
classA a;
classB b;
classC c;
};
then I need some iterator such that a method next() would give me the value
of the next element. The problem is that as you see, the values have different types.
Nope, not with the language as it is.
You could do it by deriving your classes from a common base, and then implementing your own iterator to return pointers to each item as the iterator is traversed.
Alternatively put the items in a std::vector and use that to provide the iteration.
No, there is no reflection in C++, (yet, there are murmurs about static reflection coming one day).
Anyway, there is a way to work around this, to an extent - first of all, you'll need a (temporary) tuple with references to your data members.
Then you will need a construct "iterating" over the tuple, such as:
void applyToAll() { }
template <typename Lambda, typename... Lambdas>
void applyToAll(Lambda&& closure, Lambdas&&... closures) {
std::forward<Lambda>(closure)();
applyToAll(std::forward<Lambdas>(closures)...);
}
// use your favourite sequence-making trick
template <unsigned... Is>
struct _Sequence {
typedef _Sequence<Is...> type;
};
template <unsigned Max, unsigned... Is>
struct _MakeSequence : _MakeSequence<Max - 1, Max - 1, Is...> { };
template <unsigned... Is>
struct _MakeSequence<0, Is...> : _Sequence<Is...> { };
template <typename Tuple, typename Functor, unsigned... Is>
void _foreachElemInTuple(_Sequence<Is...>, Tuple&& t, Functor&& f) {
applyToAll(
[&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
);
}
template <typename Tuple, typename Functor>
void foreachElemInTuple(Tuple&& t, Functor&& f) {
_foreachElemInTuple(
_MakeSequence<std::tuple_size<
typename std::decay<Tuple>::type>::value>(),
std::forward<Tuple>(t), std::forward<Functor>(f)
);
}
Then you can call foreachElemInTuple(yourTuple, some_adapter()).
Your adapter will look like:
struct some_adapter {
template <typename... Args>
// A little bit of C++14, you can also just -> decltype the thing
decltype(auto) operator()(Args&& ... args) const {
return doStuff(std::forward<Args>(args)...);
}
};
As everyone else says, you cannot directly iterate over data members of a
class. However, it is not difficult to do it indirectly, provided of course that
you can access each of the data members you want to iterate over. The idea
in essense, as per ScarletAmaranth's solution, is to iterate over an std::tuple
of references to those data members.
The following program shows how to obtain such a tuple, using std::forward_as_tuple,
and another way to do the iterating by compiletime recursion, without
auxiliary apparatus.
#include <tuple>
/* You want to be able do something with the values of the members of an `A`
in turn.
*/
struct A
{
char ch;
int i;
double d;
// May also have members of class type. It doesn't matter
};
/* 1) Provide yourself with the means of creating a sequence that contains
references to the data members of a given `A`
*/
std::tuple<char const &, int const &, double const &> get_A_vals(A const & a)
{
return std::forward_as_tuple(a.ch,a.i,a.d);
}
/* 2) Provide yourself with a means of applying some operation, `Func`,
to each element of an `std::tuple`
*/
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const &, Func) {}
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const & tpl, Func func)
{
func(std::get<I>(tpl));
for_each_in_tuple<I + 1>(tpl,func);
}
/* 3) Combine 1) and 2) to apply `Func` over the members of an `A`
*/
template<typename Func>
void for_each_in_A(A const & a, Func func)
{
for_each_in_tuple(get_A_vals(a),func);
}
// Testing...
#include <iostream>
// A specimen operation: just prints its argument
struct printer
{
template<typename T>
void operator () (T && t)
{
std::cout << t << std::endl;
}
};
int main()
{
A a{'a',1,2.0};
for_each_in_A(a,printer());
return 0;
}
// EOF
The program outputs:
a
1
2
If you have control of the structs or classes over whose members you need to
iterate, you may consider whether it is practical simply to dispense with them
and use the corresponding std::tuples everywhere.
Code built with gcc 4.8.2 and clang 3.3, -std=c++11.