I'm trying to implement a BitField template class for C++11/14, my base idea is:
template <typename T, size_t... Bits>
class BitField
{
public:
BitField();
private:
T value;
};
template <typename T, size_t... Bits>
BitField<T, Bits...>::BitField()
{
}
and then as an example instantiate it like this:
BitField<uint8_t, 2, 3, 3> bitfield;
where 2, 3, 3 are the sizes of the 3 bitfields, offsets come consequently (please let's ignore bit order by now).
Of course this is just a skeleton to be filled with appropriate setters and getters methods, now my question is: how can I get Bits values in the constructor to check if their sum fits the type, possibly at compile time?
More in general, can this be a good approach for this problem? I say other solutions but for many reasons I don't like them too much.
Thanks, Matteo
You can check size with (C++17)
static_assert(sizeof(T) == (Bits + ...));
Before fold-expression of C++17, there are workaround more verbose.
Create a function
template <std::size_t... Bits>
constexpr std::size_t sum()
{
const std::size_t numbers[] = {Bits...};
// std::accumulate in constexpr only since C++20
std::size_t res = 0;
for (auto number : numbers) {
res += number;
}
return res;
}
and then
static_assert(sizeof(T) == sum<Bits...>());
You can even use lambda instead of helper function if wanted.
Related
I have a Vector class which has a template of <unsigned int Dim>, eg. I can do Vector<2> for a vector in 2-dimensional space, Vector<4> for 4-dimensional, etc. I want to add some methods to the class if Dim == specific value, for instance CrossProduct for Vector<3>, or x,y,z,w getters for a vector of sufficient dimension. If used with a Vector of incorrect dimensions, I would hope to get a compile error.
I have looked around quite a lot, one of the things that I believe are close enough is std::enable_if, however, I have no clue how to use it for my particular case (where the condition is either Dim == x, or Dim > x).
Is this the right way, or is there a completely different way I should do this?
I am using C++17, by the way.
pre-C++20, static_assert might be the simplest:
template <std::size_t Dims>
struct Vector
{
// ...
double getZ() const { static_assert(Dims >= 3); return data[2]; }
};
SFINAE is possible, but complex and verbose:
template <std::size_t Dims>
struct Vector
{
// ...
template <std::size_t D = Dims, std::enable_if_t<(D >= 3) && D == Dims, int> = 0>
double getZ() const { return data[2]; }
};
Specialization is possible, but might be tricky, for example:
struct NullVector3{};
template <typename Derived>
struct Vector3
{
// ...
double getZ() const { return static_cast<Derived*>(this)->data[2]; }
};
template <std::size_t Dims>
struct Vector : std::conditional_t<(Dims >= 3), Vector3<Vector<Dims>>, NullVector3> /*, ..*/
{
// ...
// inherit of Vector3<Vector>::getZ() when Dims >= 3
// inherit of no extra method from NullVector3 else.
};
C++20 is the simplest with requires:
template <std::size_t Dims>
struct Vector
{
// ...
double getZ() const requires(Dims >= 3) { return data[2]; }
};
In C++20 you may use Constraints and Concepts.
With C++17, you need to emulate them with std::enable_if. Alternatively you could use static_assert, but I would deem enable_if to express the intention better (automatic tools like code-completion should figure enable_if out, but most probably not a static assert).
A practical example is given in this answer as found by #JHBonarius.
I would implement the methods for the generic interface and add
static_assert(Dim==3,"This method is only valid for 3-dimensional vectors.")
as the first line in each method. This will yield much cleaner compiler error message compared to std::enable_if approach.
You can also use if constexpr(Dim==x) for the rest of the method's body and to "specialize" the code based on the current dimension if varying implementations are needed.
I have a class like this:
template<std::size_t T, std::size_t... Args>
class A{
public:
std::array<int,summation<Args...>::value> x;
}
where summation is defined as:
template<std::size_t size, std::size_t... sizes>
struct summation
{
static const std::size_t value = size + summation<sizes...>::value;
};
template<std::size_t size>
struct summation<size>
{
static const std::size_t value = size;
};
The problem is that when Args is empty (i.e., I only specify the T template) the base case does not work and I get a compilation error message:
error: wrong number of template arguments (0, should be 1 or more)
How can I modify the recursion of summation to also properly handle the case when sizeof...(Args)==0 and return a value of 0 for the summation in this case? I am using C++11. Thanks
NOTE: I would also like this to work in a multithreaded environment, where summation can be simultaneously invoked by different threads with different parameters. What changes would be needed for this to work in a multithreaded environment? Thanks
The declaration should be the most generic one and then you can partially specialize with possible cases. Below solution works: https://godbolt.org/z/Ye7xEJ
template<std::size_t... sizes>
struct summation;
template<std::size_t size, std::size_t... sizes>
struct summation<size, sizes...>
{
static const std::size_t value = size + summation<sizes...>::value;
};
template<>
struct summation<> {
static const std::size_t value = 0;
};
std::size_t foo() {
return summation<1,3,4>::value;
}
std::size_t foo2() {
return summation<>::value;
}
This code:
size + summation<sizes...>::value;
translates
summation<1,2,3,4>::value
into
1 + summation<2, 3, 4>::value; // Trims out first parameter; matches variadic version
2 + summation<3, 4>::value; // Again, trims out first parameter;matches variadic version
3 + summation<4>::value; // Matches <size> version. You support signature up-to this point
4 + summation<>::value; // summation<> definition is missing in your code!!
More detailed explanation here. https://stackoverflow.com/a/48003232/1465553
I'd like to make the function multi_dimensional accept a multidimensional array by reference.
Can this be done with a variation of the syntax below which works for three_dimensional?
#include <utility>
// this works, but number of dimensions must be known (not variadic)
template <size_t x, size_t y, size_t z>
void three_dimensional(int (&nd_array)[x][y][z]) {}
// error: parameter packs not expanded with ‘...’
template <size_t... dims>
void multi_dimensional(int (&nd_array)[dims]...) {}
int main() {
int array[2][3][2] = {
{ {0,1}, {2,3}, {4,5} },
{ {6,7}, {8,9}, {10,11} }
};
three_dimensional(array); // OK
// multi_dimensional(array); // error: no matching function
return 0;
}
The main problem is that you cannot make the number of array dimensions itself variadic. So whichever way you go, you will almost certainly need a recursive approach of some sort to deal with the individual array layers. What exactly such approach should look like will mainly depend on what exactly you're planning to do with the array once it's been given to you.
If really all you want is a function that can be given any multi-dimensional array, then just write a function that can be given anything but only exists as long as that anything is an array:
template <typename T>
std::enable_if_t<std::is_array_v<T>> multi_dimensional(T& a)
{
constexpr int dimensions = std::rank_v<T>;
// ...
}
However, this by itself will most likely not get you very far. To actually do anything meaningful with the array you've been given, you will most likely need some recursive walking through subarrays. Unless you really just want to look at the topmost layer of the structure.
Another approach is to use a recursive template to peel back the individual array levels, for example:
// we've reached the bottom
template <typename T, int N>
void multi_dimensional(T (&a)[N])
{
// ...
}
// this matches any array with more than one dimension
template <typename T, int N, int M>
void multi_dimensional(T (&a)[N][M])
{
// peel off one dimension, invoke function for each element on next layer
for (int i = 0; i < N; ++i)
multi_dimensional(a[i]);
}
I would, however, suggest to at least consider using std::array<> instead of raw arrays as the syntax and special behavior of raw arrays tends to turn everything into a confusing mess in no time. In general, it might be worth to implement your own multi-dimensional array type, like an NDArray<int, 2, 3, 2> which internally works with a flattened representation and just maps multi-dimensional indices to a linear index. One advantage of this approach (besides the cleaner syntax) would be that you can easily change the mapping, e.g., to switch from row-major to column-major layout, e.g., for performance optimization…
To implement a general nD array with static dimensions, I would introduce a helper class to encapsulate the recursive computation of a linear index from an nD index:
template <std::size_t... D>
struct row_major;
template <std::size_t D_n>
struct row_major<D_n>
{
static constexpr std::size_t SIZE = D_n;
std::size_t operator ()(std::size_t i_n) const
{
return i_n;
}
};
template <std::size_t D_1, std::size_t... D_n>
struct row_major<D_1, D_n...> : private row_major<D_n...>
{
static constexpr std::size_t SIZE = D_1 * row_major<D_n...>::SIZE;
template <typename... Tail>
std::size_t operator ()(std::size_t i_1, Tail&&... tail) const
{
return i_1 + D_1 * row_major<D_n...>::operator ()(std::forward<Tail>(tail)...);
}
};
And then:
template <typename T, std::size_t... D>
class NDArray
{
using memory_layout_t = row_major<D...>;
T data[memory_layout_t::SIZE];
public:
template <typename... Args>
T& operator ()(Args&&... args)
{
memory_layout_t memory_layout;
return data[memory_layout(std::forward<Args>(args)...)];
}
};
NDArray<int, 2, 3, 5> arr;
int main()
{
int x = arr(1, 2, 3);
}
While trying to reply to this question, I found my self in the need of creating a bunch of parameters for a variadic function on the fly where:
the number of the parameters is not given
the types are all the same, but unknown (even if they must be default constructible)
At runtime, the standard containers and a for loop can be used to do that.
Anyway, I'd like to generate a set of parameters at compile time, so as to be able to forward them to a variadic function.
Because of that, a std::tuple seemed the obvious solution.
Here arose the question: given a size N and a default constructible type T at compile time, how can I write a function to generate a tuple of the given size?
I'm looking for something like this:
auto tup = gen<MyType, N>();
On SO is a notable example of a recursive generator based structs but I was struggling with a function based solution and I've not been able to find it anywhere.
A correctly written forwarding function (a la std::apply) should work with std::array<T, N> and anything else that implements the std::tuple_size/std::get interface. That said,
template<size_t, class T>
using T_ = T;
template<class T, size_t... Is>
auto gen(std::index_sequence<Is...>) { return std::tuple<T_<Is, T>...>{}; }
template<class T, size_t N>
auto gen() { return gen<T>(std::make_index_sequence<N>{}); }
Here is a possible implementation of such a function:
#include<utility>
#include<tuple>
template<typename T>
constexpr auto
params(std::index_sequence<0>) {
return std::tuple<T>{};
}
template<typename T, std::size_t I, std::size_t... O>
constexpr auto
params(std::index_sequence<I, O...>) {
auto tup = std::tuple<T>{ T{} };
auto seq = std::make_index_sequence<sizeof...(O)>{};
return std::tuple_cat(tup, params<T>(seq));
}
template<typename T, std::size_t N>
constexpr auto
gen(std::integral_constant<std::size_t, N>) {
return params<T>(std::make_index_sequence<N>{});
}
int main() {
auto tup = gen<int>(std::integral_constant<std::size_t, 3>{});
static_assert(std::tuple_size<decltype(tup)>::value == 3, "!");
}
For the sake of simplicity, I've used int as a type.
With a small effort, user defined types can be used and the constraint of having them default constructible can be relaxed.
What I want to do is:
int const bitsPerInt = log2(X);
bitset<bitsPerInt> bits(a random number...);
but I get this error:
'bitsPerInt' cannot appear in a constant expression
error: template argument 1 is invalid
If you really need this to work, make your own log2 that works in compile-time and pass it to bitset's template argument.
constexpr unsigned Log2(unsigned n, unsigned p = 0) {
return (n <= 1) ? p : Log2(n / 2, p + 1);
}
constexpr size_t bitCount = Log2(X);
std::bitset<bitCount> bits;
Live example.
Here's the solution using template meta-programming i.e. without using constexpr:
template<int N,unsigned int P=0>
struct Log2 { enum { value = Log2<N/2,P+1>::value }; };
template <unsigned p>
struct Log2<0, p> { enum { value = p }; };
template <unsigned p>
struct Log2<1, p> { enum { value = p }; };
std::bitset<Log2<4>::value> bits;
Live example.
This version should work in both C++03 and C++11; however, if you've access to C++11, I'd still recommend the constexpr way since it's cleaner (easier to understand).
Template parameter needs to be known(and constant if it is a value and not a type) at compile time. This is how templates work in C++. Templates actually generate real code for each specific version of the generic code.