How to initialize static array from std::integer_sequence? - c++

I made an iterable generator for enums that conform with the following rules:
Enum is an integer sequence, there are no gaps
Given last element of the enum is not an actual enum element
The class looks like this:
template <typename EnumType, EnumType LENGTH>
class EnumArrayNonStatic
{
public:
using ValueType = typename std::underlying_type<EnumType>::type;
//! Initialize values via private constructor
constexpr EnumArrayNonStatic() : EnumArrayNonStatic(std::make_integer_sequence<ValueType, (ValueType)LENGTH>{}) {}
//! All values generated via std::integer_sequence
EnumType values[(int)LENGTH];
private:
//! Private constructor that populates values
template <int... Indices>
constexpr EnumArrayNonStatic(std::integer_sequence<int, Indices...>) : values{(static_cast<EnumType>(Indices))...} {}
};
Usage:
enum class TestEnum
{
A,
B,
C,
D,
LENGTH
};
int main()
{
for (const TestEnum val : EnumArrayNonStatic<TestEnum, TestEnum::LENGTH>().values)
{
std::cout << (int)val << "\n";
}
return 0;
}
However, I would instead like to be able to use EnumArray<TestEnum, TestEnum::LENGTH>::values and have the values generated via template during compilation. I wrote this:
template <typename EnumType, EnumType LENGTH>
class EnumArray
{
private:
using ValueType = typename std::underlying_type<EnumType>::type;
//! Static generator of value array (returns EnumType[])
template <ValueType... Indices>
static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>) { return {(static_cast<EnumType>(Indices))...}; }
public:
//! Static array of values of an enum
static constexpr EnumType values[static_cast<ValueType>(LENGTH)] = GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH) >{});
};
I've been messing around with the code for a while but I always keep getting errors. The version above prints:
1>enumiteratortest.cpp(22): error C3108: cannot deduce a type as an initializer list is not an expression
1>enumiteratortest.cpp(25): note: see reference to function template instantiation 'auto EnumArray<TestEnum,TestEnum::LENGTH>::GenerateArray<0,1,2,3>(std::integer_sequence<int,0,1,2,3>)' being compiled
1>enumiteratortest.cpp(52): note: see reference to class template instantiation 'EnumArray<TestEnum,TestEnum::LENGTH>' being compiled
1>enumiteratortest.cpp(22): error C2440: 'return': cannot convert from 'initializer list' to 'auto'
1>enumiteratortest.cpp(22): note: There is no context in which this conversion is possible
1>enumiteratortest.cpp(25): error C2440: 'initializing': cannot convert from 'void' to 'const EnumType [4]'
1> with
1> [
1> EnumType=TestEnum
1> ]
1>enumiteratortest.cpp(25): note: There are no conversions to array types, although there are conversions to references or pointers to arrays
Surely there must be a way to initialize the array statically. Is the GenerateArray even necessary? Isn't there a way to do this?
int myArray[] = std::integer_sequence<ValueType, Indices...>{Indices...}
Or something along the lines?

You cannot initialize a language array with an initializer_list. And, you cannot change the return type of that function to an array - functions cannot return arrays.
Just change everything to std::array:
template <ValueType... Indices>
static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>)
-> std::array<EnumType, sizeof...(Indices)>
{
return {(static_cast<EnumType>(Indices))...};
}
static constexpr std::array<EnumType, static_cast<ValueType>(LENGTH)> values
= GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH)>{});

Related

deducing variadic inheritance type from constructor

I have a struct that can have a variadic number of members by inheriting from them:
struct A{ int a; };
struct B{ float b; };
struct C{ const char* c; };
template<typename ... Ts>
struct collection : Ts... {
};
int main() {
collection<A, B, C> n;
n.a = 5;
n.b = 0.5f;
n.c = "123";
}
now I want to use the initializer-list constructor:
auto n = collection<A, B, C>{ 5, 0.5f, "123" };
but I have to name the templates A, B, C. The compiler should figure this out.
I want to achieve this:
auto n = collection{ 5, 0.5f, "123" };
but this doesn't work
main.cpp(8,1): error C3770: 'int': is not a valid base class
main.cpp(12): message : see reference to class template instantiation 'collection<int,float,const char *>' being compiled
main.cpp(8,1): error C3770: 'float': is not a valid base class
main.cpp(8,1): error C3770: 'const char *': is not a valid base class
main.cpp(12,38): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'collection<int,float,const char *>'
main.cpp(12,21): message : Invalid aggregate initialization
Visual Studio 2022 17.3.0, /std:c++latest, Debug x64
Is there a (clean) way to deduce the right type? It should be possible with the right constructor, since all underlying types are uniquely assignable.
You can do this using a user defined deduction guide and a couple "meta functions" to map the parameter type to the desired class type. First the meta functions get declared like
A type_map(int);
B type_map(float);
C type_map(const char*);
and then we can create a deduction guide in the form of
template<typename... Ts>
collection(Ts... ts) -> collection<decltype(type_map(ts))...>;
and now
auto n = collection{ 5, 0.5f, "123" };
will have n being a collection<A, B, C> as seen in this live exmaple
A "meta function" is the term I use for a function that doesn't actually do anything, it is just used to map one type into another. The are only ever called in an unevaluated context like in decltype in the above example.

Why auto is not allowed in template function for creating a built-in array?

This code below does not compile:
template<typename... Ts>
void CreateArr(const Ts&... args)
{
auto arr[sizeof...(args) + 1]{ args... };
}
int main()
{
CreateArr(1, 2, 3);
}
due to the following errors:
'arr': in a direct-list-initialization context, the type for 'auto [6]' can only be deduced from a single initializer expression
auto [6]': an array cannot have an element type that contains 'auto'
'initializing': cannot convert from 'const int' to 'std::initializer_list<int>'
My questions are:
Why cannot I use auto to define the type of the array?
How to define it properly to work with the template?
Why cannot I use auto to define the type of the array?
For the same reason, following does not work/ allowed!
auto ele[]{ 1, 2, 3 };
More reads: Why can't I create an array of automatic variables?
How to define it properly to work with the template?
Use the std::common_type_t for specifying the type
#include <type_traits> // std::common_type_t
template<typename... Ts>
void CreateArr(const Ts&... args)
{
std::common_type_t<Ts...> arr[sizeof...(args)]{ args... };
static_assert(std::is_array_v<int[sizeof...(args)]>, "is not array!");
}
(See a Live Demo)

Initialize a constexpr array with user-defined literal

Simplified version
class C {
public:
static constexpr std::array<C, 2> foo {{"1"_C, "2"_C}};
int x;
constexpr C(char c) { x=c; }
}
constexpr C operator"" _C(const char * str, size_t n) { return C(*str); }
This doesn't fly, because the literals are not understood at the line where the array is defined. But the free literal function can't be moved earlier because then C isn't known.
Is there a solution to this Gordian knot that doesn't involve adding variadic templates or something horrid like that into the code?
The problem doesn't really lie with user-defined literals, but with the fact that std::array requires complete types (or really, any constexpr initialization does). The following code will also fail to compile:
#include <array>
class C {
public:
static constexpr std::array<C, 2> foo {{C('1'), C('2')}};
int x;
constexpr C(char c) : x(c) {} // please use a mem-initializer-list
};
With errors similar to (Clang 3.3 SVN here):
/usr/include/c++/v1/array:136:16: error: field has incomplete type 'value_type'
(aka 'C')
value_type __elems_[_Size > 0 ? _Size : 1];
^
t.cpp:5:36: note: in instantiation of template class 'std::__1::array'
requested here
static constexpr std::array<C, 2> foo {{C('1'), C('2')}};
^
t.cpp:3:7: note: definition of 'C' is not complete until the closing '}'
class C {
^

How to expose boost::tuples::tuple to Java bindings?

I have a list of boost::tuple. I want to expose this tuple list to Java bindings through SWIG. But when I try to compile mt wrap.cxx, generated by SWIG, I get following errors:
d:\xyz\...\vector.h(115) : error C2678: binary '==' : no operator found which takes a left-hand operand of type 'const boost::tuples::tuple<T0,T1>' (or there is no acceptable conversion)
with
[
T0=std::string,
T1=std::string
]
c:\program files\microsoft visual studio 8\vc\platformsdk\include\guiddef.h(192): or 'int operator ==(const GUID &,const GUID &)'
while trying to match the argument list '(const boost::tuples::tuple<T0,T1>, const MyTuple)'
with
[
T0=std::string,
T1=std::string
]
d:\xyz\...\vector.h(111) : while compiling class template member function 'int Vector<T>::index(const T &) const'
with
[
T=MyTuple
]
d:\xyz\...\MyTuple_wrap.cxx(17731) : see reference to class template instantiation 'Vector<T>' being compiled
with
[
T=MyTuple
]
Can anyone tell me what I should do to resolve this issue?
It is unclear how you arrived at the error you've shown. boost::tuple is tricky to wrap by default and there doesn't seem to be any standard interface to it included with SWIG. In my tests I couldn't get close to the error you were seeing without manually writing an interface file.
I did however succeeded in wrapping boost's tuples using the following interface file:
%{
#include <boost/tuple/tuple.hpp>
%}
namespace boost {
template <typename T1=void, typename T2=void, typename T3=void>
struct tuple;
template <>
struct tuple<void,void,void> {
};
template <typename T1>
struct tuple<T1, void, void> {
tuple(T1);
%extend {
T1 first() const {
return boost::get<0>(*$self);
}
}
};
template <typename T1, typename T2>
struct tuple <T1, T2, void> {
tuple(T1,T2);
%extend {
T1 first() const {
return boost::get<0>(*$self);
}
T2 second() const {
return boost::get<1>(*$self);
}
}
};
template <typename T1, typename T2, typename T3>
struct tuple <T1,T2,T3> {
tuple(T1,T2,T3);
%extend {
T1 first() const {
return boost::get<0>(*$self);
}
T2 second() const {
return boost::get<1>(*$self);
}
T3 third() const {
return boost::get<2>(*$self);
}
}
};
}
Basically all it does is add accessor functions to each of the specialisations of tuple you might care about. It's sufficient to make it minimally useful in Java or some other language. You would want to expand on this to cover larger tuples. You probably want to make the member functions get/set if your tuples aren't intended to be immutable.
I was able to test this with a SWIG module:
%module test
%include "boost_tuple.i"
%template(TestTuple) boost::tuple<int, double, char>;
%template(SingleTuple) boost::tuple<char>;
%inline %{
boost::tuple<int, double, char> func1() {
return boost::make_tuple(3, 2.0, '1');
}
void test1(boost::tuple<int, double, char>) {
}
%}
Which worked as expected with the following Java:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
TestTuple t = test.func1();
System.out.println("1: " + t.first() + " 2: " + t.second() + " 3: " + t.third());
test.test1(test.func1());
test.test1(new TestTuple(0, 0.0, '0'));
}
}

Compilation error with inheritance of templates accessed through pointer of pointers

Template compilation error using pointer of pointer: (What causes the code not to compile ??)
template <int DIM> class Interface { };
template <int DIM> class Implementation : public Interface<DIM> { };
template <int DIM> class Modifier {
public: void modify(const Interface<DIM>** elem) { } // only one * compiles !!
};
template <int DIM> class Caller {
Modifier<DIM> modifier;
Implementation<DIM>** impl; // only one * compiles !!
public: void call() { modifier.modify(impl); }
};
void main() {
Caller<-13> caller;
caller.call(); // also compiles with this line commented
}
Gives this compilation error (on Visual Studio 1988 professional):
imlgeometry.cpp(-10) : error C2664: 'Modifier<DIM>::modify' : cannot convert parameter 1 from 'Implementation<DIM> **' to 'const Interface<DIM> **'
with
[
DIM=-23
]
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
imlgeometry.cpp(-16) : while compiling class template member function 'void Caller<DIM>::call(void)'
with
[
DIM=-29
]
imlgeometry.cpp(-17) : see reference to class template instantiation 'Caller<DIM>' being compiled
with
[
DIM=-34
]
The problem is that in C++ it is not legal (or safe) to convert a T** to a const T**. The reason is that if you could do this, you would end up being able to subvert const. For example:
const T value;
T* mutablePtr;
const T** doublePtr = &mutablePtr; // Illegal, you'll see why.
*doublePtr = &value; // Legal, both sides have type const int*.
// However, mutablePtr now points at value!
*mutablePtr = 0; // Just indirectly modified a const value!
To fix this, you'll need to update your code so that you aren't trying to do this conversion. For example, you might change the parameter type of modify to be
const Interface<DIM> * const *
Since it is legal to convert T** to const T* const*.
Hope this helps!