Adding new constructors to a specialised template class - c++

I have a class defining an array of fixed length n, with some methods.
template<int n>
struct array_container{
/* some code here */
int array[n];
};
Let's say I want to add a constructor to array_container<3>, something along the lines of:
array_container<3>::array_container(int a0, int a1 ,int a2){
array[0] = a0;
array[1] = a1;
array[2] = a1;
}
I know of two ways to do this:
One is to copy the entire code of the generic class, replacing n with 3, and add my constructor:
template<>
struct array_container<3>{
/* some code here */
int array[3];
array_container(int a0, int a1 ,int a2){
array[0] = a0;
array[1] = a1;
array[2] = a1; }
};
This works correctly, but has the disadvantage of needing to copy all the code and methods from the generic base.
Another method is to add a constructor array_container(int a0, int a1, int a2); in the generic class, then define:
template<>
array_container<3>:: array_container(int a0, int a1 ,int a2){
array[0] = a0;
array[1] = a1;
array[2] = a1; }
This has the disadvantage of populating my generic base class with at best undefined or at worst incorrect constructors, such as
array_container<2>(int a0, int a1 ,int a2) (undefined or incorrect depending on whether or not I add the definition to the generic base or not).
Is there any approach that avoids both pitfalls? Ie. doesn't need to copy-paste the entire generic base code for the specialization, and doesn't add unnecessary constructors to the generic base?

Why not simply use
template <std::size_t size>
struct MyArrayWithFunctions : std::array<int, size> {
/* some functions */
};
std::array allows aggregate initialization and even deduces the size so you can simply write
MyArrayWithFunctions arr{1,2,3};
You can add a deduction guideline for your own class but why re-implement array?

Is there any approach that avoids both pitfalls? Ie. doesn't need to copy-paste the entire generic base code for the specialization, and doesn't add unnecessary constructors to the generic base?
Ignoring the fact that, as #Goswin von Brederlow mentions, you seem to be reinventing the wheel (std::array and aggregate initialization), C++20's requires-expressions allows you to define constructors in a primary template that are constrained to only certain specializations. E.g.:
#include <type_traits>
// Helper trait for constraining a ctor to a set
// of specializations.
template <int n, int... ns> struct is_one_of {
static constexpr bool value{false};
};
template <int n, int n0, int... ns> struct is_one_of<n, n0, ns...> {
static constexpr bool value{(n == n0) || is_one_of<n, ns...>::value};
};
template <int n, int... ns>
inline constexpr bool is_one_of_v{is_one_of<n, ns...>::value};
template <int n> struct array_container {
/* some code here */
int array[n];
// Constrained to n == 3 or n == 5.
template <typename... Args>
requires(std::is_same_v<Args, int> &&...) && (sizeof...(Args) == n) &&
is_one_of_v<n, 3, 5 /*, ... */> array_container(Args... args)
: array{args...} {}
};
// Demo.
array_container<3> arr3{1, 2, 3}; // OK
array_container<4> arr4{1, 2, 3, 4}; // Error (no matching ctor)
array_container<5> arr5{1, 2, 3, 4, 5}; // OK

The easier solution would be to construct it with an array(in place).
template<int n>
struct array_container{
int array[n];
array_container(std::array<int, n> arrayIn)
{
std::copy(arrayIn.begin(), arrayIn.end(), array);
}
};
Otherwise you can mess with variadic templates and parameter unpacking.

If happen to have a C++17 compiler you can use the following code:
#include <type_traits>
// class definition
template<int n>
struct array_container {
int array[n];
template<typename... Args, typename std::enable_if_t<(std::is_same_v<int, Args> && ...) && (sizeof...(Args) == n), bool> = true>
array_container(Args... args):
array{args...}
{}
};
// user defined template deduction guide
template<typename... Args>
array_container(Args...) -> array_container<sizeof...(Args)>;
and use it like
array_container<3> x {1,2,3};
or even let the size be deduced from the number of arguments like
// n deduced to 3 from the number of arguments
array_container x {1,2,3};
The constructor is a variadic template, taking any number of int arguments (the latter is enforced by the std::enable_if_t template parameter) and initializes the array member from them. A user defined deduction guide can be used to automatically deduce the n parameter from the number of arguments, that you pass to the constructor.
See it live on godbolt.

Related

string literals as template arguments forces code duplication and verbosity

A compile-time created string is being used as a character array in a structure and its max size is driven by that string. Also that structure is being used as a member variable in another struct, and there are multiple of such string buffer structs in it, which in turn drives its storage size.
Using strings as template arguments does work, but introduces multiple code repetitions, spreading like wild fire through the source code. Matters get even worse when the first structure has multiple strings as template arguments (2 or even 3).
Here's a working example:
#include <cstddef>
static constinit decltype(auto) c = "abc";
//static constinit decltype(auto) c2 = "12";
// size of A is driven by compile-time-created strings
template<typename T, size_t N /*, typename T2, size_t N2*/>
struct A{
char str[N] {};
// char str2[N2] {};
consteval A(const T(&s)[N] /*, const T2(&s2)[N2]*/) {
for(int i = 0; i < N; ++i){
str[i] = s[i];
if(s[i] == 0) // for strings shorter than storage
break;
}
// for(int i = 0; i < N2; ++i){
// str2[i] = s2[i];
// if(s2[i] == 0)
// break;
// }
}
};
// dummy function which's sole purpose is to help with the type deduction
template<typename T, size_t N /*, typename T2, size_t N2*/>
consteval auto f(const T(&s)[N] /*, const T2(&s2)[N2]*/){
return A<T, N /*, T2, N2*/>(s /*, s2*/);
}
// size of B's members and struct's total size are driven by compile-time
// created strings
struct B{
// explicit (manual) error-prone template params
A<char, 4 /*, char, 3*/> xxx{c /*, c2*/};
// use of a dummy function feels very wrong
decltype(f(c /*, c2*/)) yyy{c /*, c2*/};
// also uses dummy function
decltype(f("abc" /*, "12"*/)) zzz{"abc" /*, "12"*/};
// would like to be able to use shorter strings
//A<char, 4 /*, char, 3*/> fail{"a" /*, "1"*/};
};
The 3 working examples of usage are either prone to mistakes or introduce too much code repetition. And the last one, that is not working, is a nice-to-have sort of thing along the lines of "I wonder if this is doable?" to me.
Link to Compiler Explorer
Is there anything I'm missing in the C++ standard that would let me avoid repetition while retaining this automation to avoid potential manual mistakes?
Ideally I'd've loved to just code like this snippet below:
struct B{
// automatic type deduction without some dummy function's help
A xxx{c /*, c2*/};
// strings in multiple TUs produce independent templates in current standard :(
A<"abc" /*, c2*/> xxx;
};
// usage
// constructs with { {c /*, c2*/}, {"abc" /*, c2*/} }
constinit B b;
// constructs with { {c /*, c2*/}, {"bc" /*, "0"*/} }
constinit B b2{{}, {"bc" /*, "0"*/}};
Without using standard library headers (excepting <cstddef>), here's a definition for the class template A that gets most of the desired features with as little boilerplate as possible, given that C++ disallows automatic argument deduction in non-static struct member declarations. This uses a c++17 user-defined deduction guide:
#include <cstddef>
template <typename T, std::size_t N>
struct A {
// + 1 to account for null termination in storage
T str[N + 1];
template <std::size_t... Ns>
// to prevent aggregate construction from compiling
// if sum of string literal sizes exceed storage capacity
requires(N + 1 > (0 + ... + (Ns - 1)))
consteval A(const T (&...s)[Ns]) {
auto it = str;
(..., [&](const auto &r) {
for (const auto c : r) {
if (!c) break;
*it++ = c;
}
}(s));
*it = T{};
}
};
// user-defined deduction guide
template <typename T, std::size_t... Ns>
// - 1 to exclude null termination from each string literal in the pack
A(const T (&...s)[Ns]) -> A<T, (0 + ... + (Ns - 1))>;
Usage and tests below, with obligatory Compiler Explorer link:
static constinit decltype(auto) c1 = "abc";
static constinit decltype(auto) c2 = "12";
struct B {
// no dummy function
// but requires repetition of the compile-time constants
// in the type expression
decltype(A{c1, c2}) value{c1, c2};
};
#include <concepts>
// deduces correct width from compile-time constants
static_assert(std::same_as<decltype(B{}.value), A<char, 5>>);
#include <string_view>
using namespace std::string_view_literals;
// default construction
static_assert(B{}.value.str == "abc12"sv);
// aggregate construction
static_assert(B{{}}.value.str == ""sv);
static_assert(B{{c1}}.value.str == "abc"sv);
static_assert(B{{c2}}.value.str == "12"sv);
static_assert(B{{c2, c1}}.value.str == "12abc"sv);
static_assert(B{{"a", "1"}}.value.str == "a1"sv);
// candidate template ignored: constraints not satisfied [with Ns = <3, 5>]
// because '5UL + 1 > 0 + (3UL - 1) + (5UL - 1)' (6 > 6) evaluated to false
// static_assert(B{{"ab", "1234"}}.value.str == "ab1234"sv);
To completely eliminate duplication in the declaration, you can create a derived class that behaves like A{c1, c2}. To make the linkage well-behaved, you'll need to canonicalize the template arguments of the derived class. Here's one way to do that:
template <typename T, T... Cs>
struct C : decltype(A{{Cs..., T{}}}) {
using base = decltype(A{{Cs..., T{}}});
using base::base;
constexpr C() : base{{Cs..., T{}}} {}
};
#include <utility>
template <A, typename...>
struct to_c;
template <typename T, std::size_t N, A<T, N> a>
struct to_c<a> : to_c<a, std::make_index_sequence<N>> {};
template <typename T, std::size_t N, A<T, N> a, std::size_t... Is>
struct to_c<a, std::index_sequence<Is...>> {
using type = C<T, a.str[Is]...>;
};
template <A... a>
using to_c_t = typename to_c<A{a.str...}>::type;
static constinit decltype(auto) c1 = "abc";
static constinit decltype(auto) c2 = "12";
#include <concepts>
static_assert(std::same_as<to_c_t<c1, c2>, C<char, 'a', 'b', 'c', '1', '2'>>);
For your purposes, to_c_t<c1, c2> value; should behave almost like decltype(A{c1, c2}) value{c1, c2};, except there's no duplication, and it is safe to use across translation units since it is an alias for the canonical type C<char, 'a', 'b', 'c', '1', '2'>. Just for completeness, here's the full example using this approach.

Automatic generation of a brace-enclosed initializer list in C++ using language features (NOT pre-processor directives)

I'm looking for a solution using only native C++ language features (up to C++17) for accomplishing the following:
std::array<Type, unsigned int Elem> array_{Type(), // 1 - Call constructor on Type()
Type(), // 2 - ...
... , // 3 - ...
Type()} // Elems - Call the Elem:th Type() constructor
In addition, what I'd also like is that each constructor call should be able to take an arbitrary number of arguments.
A concrete example would be to automate the writing of the following:
std::array<std::shared_ptr<int>, 4> array_{std::make_shared<int>(),
std::make_shared<int>(),
std::make_shared<int>(),
std::make_shared<int>()}
I.e., provided that I know Type and Elem, I'd like to automate the process of creating the brace-enclosed initializer list and in the process call Type:s constructor.
Any ideas?
Update, the real problem I'd like to solve is the following:
template <typename Type, unsigned int Size>
class Storage {
public:
Storage(std::initializer_list<Type> initializer) : array_{initializer} {}
private:
std::array<Type, Size> array_;
};
void foo(){
Storage<std::shared_ptr<int>, 100> storage(...);
// or perhaps
auto storage = std::make_shared<Storage<std::shared_ptr<int>, 100>>(here be an initializer list containing calls to 100 std::make_shared<int>());
}
Like this:
#include <array>
#include <memory>
#include <utility>
template <std::size_t ...I>
std::array<std::shared_ptr<int>, sizeof...(I)> foo(std::index_sequence<I...>)
{
return {(void(I), std::make_shared<int>())...};
}
std::array<std::shared_ptr<int>, 4> array_ = foo(std::make_index_sequence<4>());
Guaranteed copy elision from C++17 ensures that the array is constructed in place, and no extra moves happen.
The return statement expands to {(void(0), std::make_shared<int>()), (void(1), std::make_shared<int>())...}. void(...) is not strictly necessary, but Clang emits a warning otherwise.
But if it was my code, I'd write a more generic helper instead:
#include <utility>
template <typename R, typename N, typename F, N ...I>
[[nodiscard]] R GenerateForEach(std::integer_sequence<N, I...>, F &&func)
{
return {(void(I), func(std::integral_constant<N, I>{}))...};
}
template <typename R, auto N, typename F>
[[nodiscard]] R Generate(F &&func)
{
return (GenerateForEach<R, decltype(N)>)(std::make_integer_sequence<decltype(N), N>{}, std::forward<F>(func));
}
Then:
auto array_ = Generate<std::array<std::shared_ptr<int>, 4>, 4>([](auto){return std::make_shared<int>();});
While the lambda discards the index in this case, it often ends up being useful, especially given that in [](auto index){...}, index.value is constexpr.

How to disable a member function based on a class template parameter?

To illustrate the situation, let's assume a minimal example: a Vector template class taking its dimension as a non-type template parameter. This class will provide x(), y() (etc.) accessors when the dimension allows it:
template <int N_dimension>
class Vector
{
public:
// ctors, etc.
int &x();
template <class = std::enable_if_t<(N_dimension>2)>> int &y();
private:
std::array<int, N_dimension> mData;
};
Yet, this does not work, because enable_if can only be applied on deduced template parameters.
Our current workaround looks cumbersome:
template <int N=N_dimension, class = std::enable_if_t<(N>2)>> int &y();
Moreover, it also requires a static-assert in the definition to make sure it is fool-proof (because now client code could give an explicit value to N that does not match the real dimension. Edit: Or an explicit value for the anonymous second template parameter, as pointed out by SergeyA).
Is there a more direct approach to express this in C++?
I would do away with SFINAE here, and simply split the code into interface and private implementation like that:
int& y() {
return y_impl(std::bool_constant<N > 2>{});
}
private:
int& y_impl(std::true_type ) {
// impl
}
int& y_impl(std::false_type ) {
static_assert(N > 2 /* always false */, "Wrong number of dimensions!");
}
The split here is in assumption of y not compilable when N <= 2, to reduce the clutter of error messages. If this is not the case, a single static_assert in y body would be sufficient.
In C++20, you might simply use requires to discard method:
template <int N>
class Vector
{
public:
int &x();
int &y() requires(N >= 2);
private:
std::array<int, N_dimension> mData;
};
In previous version, it is more verbose:
template <std::size_t N>
class Vector
{
public:
int &x();
template <std::size_t M = N, std::enable_if_t<(M >= 2 && M == N), int> = 0>
int &y();
private:
std::array<int, N_dimension> mData;
};

Call sequence of template function for sequence of template parameters

Let's imagine I have several template functions, e.g.:
template <int I> void f();
template <int I> void g();
template <int I> void h();
How can I call sequence of any of these functions for sequence of template parameters?
In other words, I need such behaviour:
{some template magic}<1, 5>(f); // This is pseudocode, I don't need exactly this format of calling.
unrolls into:
f<1>();
f<2>();
f<3>();
f<4>();
f<5>();
And I need the same method to work for every of my functions (not only for f, but for g and h too) without writing big awkward structure for every of these functions.
I can use C++11, and even already implemented in latest development gcc version C++1y/C++14 functionality (http://gcc.gnu.org/projects/cxx1y.html), e.g. polymorphic lambdas.
With C++1y features. Instead of calling the function directly and passing the template argument as, well, a template argument, you can create a lambda that takes a function argument which contains the template argument as part of its type. I.e.
f<42>();
[](std::integral_constant<int, 42> x) { f<x.value>(); }
[](auto x) { f<x.value>(); }
With this idea, we can pass the function template f around, when wrapped into such a polymorphic lambda. That's possible for any kind of overload set, one of the things you can't do with ordinary lambdas.
To call f with a sequence of template arguments, we'll need the common indices classes for the indices expansion trick. Those will be in the C++1y Standard Library. Coliru's clang++ compiler for example still uses an older libstdc++ which doesn't have them AFAIK. But we can write our own:
#include <utility>
using std::integral_constant;
using std::integer_sequence; // C++1y StdLib
using std::make_integer_sequence; // C++1y StdLib
// C++11 implementation of those two C++1y StdLib classes:
/*
template<class T, int...> struct integer_sequence {};
template<class T, int N, int... Is>
struct make_integer_sequence : make_integer_sequence<T, N-1, N-1, Is...> {};
template<class T, int... Is>
struct make_integer_sequence<T, 0, Is...> : integer_sequence<T, Is...> {};
*/
When we write make_integer_sequence<int, 5>, we'll get a type that's derived from integer_sequence<int, 0, 1, 2, 3, 4>. From the latter type, we can deduce the indices:
template<int... Indices> void example(integer_sequence<int, Indices...>);
Inside this function, we have access to the indices as a parameter pack. We'll use the indices to call the lamba / function object f as follows (not the function template f from the question):
f( integral_constant<int, Indices>{} )...
// i.e.
f( integral_constant<int, 0>{} ),
f( integral_constant<int, 1>{} ),
f( integral_constant<int, 2>{} ),
// and so on
Parameter packs can only be expanded in certain contexts. Typically, you'd expand the pack as initializers (e.g. of a dummy array), as the evaluation of those is are guaranteed to be ordered (thanks, Johannes Schaub). Instead of an array, one could use a class type such as
struct expand { constexpr expand(...) {} };
// usage:
expand { pattern... };
A dummy array looks like this:
int expand[] = { pattern... };
(void)expand; // silence compiler warning: `expand` not used
Another tricky part is to deal with functions returning void as the pattern. If we combine a function call with a comma operator, we always get a result
(f(argument), 0) // always has type int and value 0
To break any existing overloaded comma operators, add a void()
(f(argument), void(), 0)
Finally, combine all the above to create magic:
template<int beg, class F, int... Is>
constexpr void magic(F f, integer_sequence<int, Is...>)
{
int expand[] = { (f(integral_constant<int, beg+Is>{}), void(), 0)... };
(void)expand;
}
template<int beg, int end, class F>
constexpr auto magic(F f)
{
// v~~~~~~~v see below (*)
return magic<beg>(f, make_integer_sequence<int, end-beg+1>{});
}
Usage example:
#include <iostream>
template<int N> void f() { std::cout << N << "\n"; }
int main()
{
//magic<1, 5>( [](auto x) { f<decltype(x)::value>(); } );
magic<1, 5>( [](auto x) { f<x.value>(); } );
}
(*) IMHO end-beg+1 is bad practice. There's a reason why the StdLib works with half-open ranges of the form [begin, end): The empty range simply is [begin, begin). With the StdLib using half-open ranges, it might be inconsistent to use closed ranges here. (There's one exception in the StdLib I know of, it has to do with PRNGs and the maximum integer value.)
I'd suggest you'd design your magic interface to take half-open ranges, i.e.
magic<1, 6>( [](auto x) { f<x.value>(); } ); // [1, 6) i.e. {1,2,3,4,5}
with the implementation
template<int beg, int end, class F>
constexpr auto magic(F f)
{
// v~~~~~v
return magic<beg>(f, make_integer_sequence<int, end-beg>{});
}
Note the weird +1 disappears.
Using reified functions and template template arguments:
#include <iostream>
template<int I> class f {
public:
static void call() {
std::cout << I << '\n';
}
};
template<template<int I> class X, int I, int J> class magic {
public:
static void call() {
X<I>::call();
magic::call();
}
};
template<template<int I> class X, int I> class magic<X,I,I> {
public:
static void call() {
X<I>::call();
}
};
int main(int argc, char** argv) {
magic<f,2,6>::call();
return 0;
}

less verbose way to declare multidimensional std::array

Short question:
Is there a shorter way to do this
array<array<atomic<int>,n>,m> matrix;
I was hoping for something like
array< atomic< int>,n,m> matrix;
but it doesnt work...
A template alias might help out:
#include <array>
template <class T, unsigned I, unsigned J>
using Matrix = std::array<std::array<T, J>, I>;
int main()
{
Matrix<int, 3, 4> matrix;
}
A palatable workaround for compilers that don't support template aliases yet is to use a simple metafunction to generate the type:
#include <cstddef>
#include <array>
template<class T, std::size_t RowsN, std::size_t ColumnsN>
struct Matrix
{
typedef std::array<std::array<T, ColumnsN>, RowsN> type; // row major
private:
Matrix(); // prevent accidental construction of the metafunction itself
};
int main()
{
Matrix<int, 3, 4>::type matrix;
}
Solution using variadic templates (slightly more complex than the template alias, but more general purpose)
template <typename T, std::size_t thisSize, std::size_t ... otherSizes>
class multi_array : private std::array<multi_array<T, otherSizes...>, thisSize>
{
using base_array = std::array<multi_array<T, otherSizes...>, thisSize>;
public:
using base_array::operator[];
// TODO: add more using statements to make methods
// visible. This is less typing (and less error-prone)
// than forwarding to the base_array type.
};
template <typename T, std::size_t thisSize>
class multi_array<T, thisSize> : private std::array<T, thisSize>
{
using base_array = std::array<T, thisSize>;
public:
using base_array::operator[];
// TODO: add more using statements to make methods
// visible. This is less typing (and less error-prone)
// than forwarding to the base_array type.
};
There might be some improvement on assigning to non-leaves of the array that could be made.
I tested with a relatively recent build of clang/LLVM.
Enjoy!
When nested, std::array can become very hard to read and unnecessarily verbose. The opposite ordering of the dimensions can be especially confusing.
For example:
std::array < std::array <int, 3 > , 5 > arr1;
compared to
char c_arr [5][3];
Also, note that begin(), end() and size() all return meaningless values when you nest std::array.
For these reasons I've created my own fixed size multidimensional array containers, array_2d and array_3d. They have the advantage that they work with C++98.
They are analogous to std::array but for multidimensional arrays of 2 and 3 dimensions. They are safer and have no worse performance than built-in multidimensional arrays. I didn't include a container for multidimensional arrays with dimensions greater than 3 as they are uncommon. In C++11 a variadic template version could be made which supports an arbitrary number of dimensions (Something like Michael Price's example).
An example of the two-dimensional variant:
//Create an array 3 x 5 (Notice the extra pair of braces)
fsma::array_2d <double, 3, 5> my2darr = {{
{ 32.19, 47.29, 31.99, 19.11, 11.19},
{ 11.29, 22.49, 33.47, 17.29, 5.01 },
{ 41.97, 22.09, 9.76, 22.55, 6.22 }
}};
Full documentation is available here:
http://fsma.googlecode.com/files/fsma.html
You can download the library here:
http://fsma.googlecode.com/files/fsma.zip
Here's a simple, generic version:
template <typename T, size_t d1, size_t d2, size_t... ds>
struct GetMultiDimArray
{
using type = std::array<typename GetMultiDimArray<T, d2, ds...>::type, d1>;
};
template <typename T, size_t d1, size_t d2>
struct GetMultiDimArray<T, d1, d2>
{
using type = std::array<std::array<T, d2>, d1>;
};
template <typename T, size_t d1, size_t d2, size_t... ds>
using MultiDimArray = typename GetMultiDimArray<T, d1, d2, ds...>::type;
// Usage:
MultiDimArray<int, 3, 2> arr {1, 2, 3, 4, 5, 6};
assert(arr[1][1] == 4);