I have this code in my source:
template <std::size_t... Dims>
class DimensionPack {
public:
using Dimensions = std::index_sequence<Dims...>;
static const std::size_t total_dimensions = sizeof...(Dims);
std::vector<unsigned int> even_or_odd;
public:
DimensionPack() {
unsigned idx = 0;
for ( ; idx < total_dimensions; idx++ ) {
//MatrixDimensionOddOrEven mdoe( Dimensions[idx] );
//unsigned int val = mdoe.even_or_odd;
//even_or_odd.push_back( val );
}
}
};
The commented lines of code is the code in question. I'm not familiar with using std::indx_sequence<> and I've read the documentation from MSD on it and still not sure about it especially when it is used with the using directive.
This template class will be used as a parameter pack for other variadic template classes for storing and extracting the data from the variadic parameter list. Within this class's constructor I'm using this structure's constructor and private method to check values and to return its state of either being even or odd and getting the value from its public constant member:
struct MatrixDimensionOddOrEven {
const unsigned int even_or_odd;
explicit MatrixDimensionOddOrEven( unsigned int odd_or_even ) : even_or_odd( test( odd_or_even ) ) {}
private:
const unsigned int test( unsigned int value ) const {
if ( value == 0 ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << "invalid number: " << value << " must be >= 1.";
Logger::log( strStream, Logger::TYPE_ERROR );
throw ExceptionHandler( strStream );
}
return ( ((value % 2) == 0) ? ODD : EVEN );
}
};
The cpp file to this header file compiles, but when I go to compile another cpp file such as main that includes it; it fails to compile.
This is the current error messages that I'm getting:
1>------ Build started: Project: FileTester, Configuration: Debug Win32 ------
1> main.cpp
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(44): error C2540: non-constant expression as array bound
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(41): note: while compiling class template member function 'DimensionPack<2,3,4>::DimensionPack(void)'
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\main.cpp(336): note: see reference to function template instantiation 'DimensionPack<2,3,4>::DimensionPack(void)' being compiled
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(60): note: see reference to class template instantiation 'DimensionPack<2,3,4>' being compiled
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\main.cpp(155): note: see reference to class template instantiation 'Matrix<float,2,3,4>' being compiled
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): error C2228: left of '.even_or_odd' must have class/struct/union
1>c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): error C2789: 'val': an object of const-qualified type must be initialized
1> c:\users\skilz80\documents\visual studio 2015\projects\filetester\filetester\matrix.h(45): note: see declaration of 'val'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
It isn't so much the compiler errors that are giving me trouble; it is more about this syntax that is sort of new to me within the use of variadic templates and parameter packs. So how do I properly write the syntax to get the individual elements of this std::index_sequence<...> that is assigned to Dimensions with the using directive in order to pass those values to the constructor of the helper structure that is seen in the for loop within the DimensionPack class?
There is no need for index_sequence. This will do:
template <std::size_t... Dims>
class DimensionPack {
public:
std::vector<unsigned int> even_or_odd;
public:
DimensionPack()
: even_or_odd{MatrixDimensionOddOrEven{Dims}.even_or_odd...}
{
}
};
As a bonus, the beauty of it is that you don't need to push_back each element. You can directly initialize the vector with all the elements.
Why would you do this?
template <std::size_t... Dims>
class DimensionPack {
public:
using odd_dims = std::integer_sequence<bool,
std::enable_if_t<Dims!=0, bool>(Dims%2)...
>;
constexpr static std::array<bool, sizeof...(Dims)> get_odd_dims() {
return {{ (bool)(Dims%2)... }};
}
};
now odd_dims is a integer_sequence of which dimensions are even and odd.
get_odd_dims returns a constexpr array of bools of which dimensions are odd. This is easier to iterate over than a integer sequence.
The odd_dims type will fail to compile if any Dims are 0. No need for runtime checks.
Dynamic allocation here on a per-DimensionPack basis seems a very strange way to solve this.
Related
I want to pass a lambda to a function, but I have run into a problem of successfully passing it onto the function. The function chooses to append TrueVal or FalseVal and creates a vector of boolean, based on the given condition.
I'm using 2019 Visual Studio's ISO C++14 Standard to compile the code.
#include <iostream>
#include <vector>
using namespace std;
template<typename T, typename T1, typename T2>
vector<bool> ConstructNestedVectorByElements(T condition, T1 TrueVal, T2 FalseVal) {
vector<bool> TempCol;
TempCol = {};
for (int i = 0; i < 3; i++)
{
if (condition(i)) {
TempCol.emplace_back(TrueVal);
}
else {
TempCol.emplace_back(FalseVal);
}
}
return TempCol;
}
int main()
{
vector<int> NumList = { 0, 1, 2 };
vector<bool> BoolList = {true, false, true};
auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, false); //this works
auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement); //error
return 0;
};
The error message:
C2440 'initializing': cannot convert from 'T2' to 'bool' ...\include\vector line 2385
1>...\vector(2385,18): error C2440: 'initializing': cannot convert from 'T2' to 'bool'
1> with
1> [
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
1>...\vector(2385,18): message : No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>...\Source.cpp(19): message : see reference to function template instantiation 'decltype(auto) std::vector<bool,std::allocator<bool>>::emplace_back<T2&>(T2 &)' being compiled
1> with
1> [
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
1>...\Source.cpp(36): message : see reference to function template instantiation 'std::vector<std::vector<bool,std::allocator<bool>>,std::allocator<std::vector<bool,std::allocator<bool>>>> ConstructNestedVectorByElements<main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,bool,main::<lambda_e116e485fb739b952327b9205614af81>>(T,T1,T2)' being compiled
1> with
1> [
1> T=main::<lambda_52b07f243bfcbbd5a342ddead4700eca>,
1> T1=bool,
1> T2=main::<lambda_e116e485fb739b952327b9205614af81>
1> ]
template <class... _Valty>
decltype(auto) emplace_back(_Valty&&... _Val) {
bool _Tmp(_STD forward<_Valty>(_Val)...);
push_back(_Tmp);
I think the problem might be one of the following:
I'm passing more than one type of argument into T2 (a lambda and a bool): Perhaps I used the wrong keyword, typename, to initialize T2? I tried with class but the same thing occurred.
OriginalElement isn't given parameters when it requires them: This confuses me a bit. If I change the line to:
TempCol.emplace_back(FalseVal(i, j)); //this is line 19
This error shows up:
C2064 term does not evaluate to a function taking 2 arguments ...\Source.cpp line 19
However, this seems not to be the case for condition(i, j), which compiles correctly. Is there a difference in handling (what I assume to be) boolean when in a conditional, and when appending it to a vector?
Lambdas not being constexpr, so it can't be used in templates: I don't really understand it, but there seems to be some relationship with this topic: (1, 2, 3)
The issue is that OriginalElement is not a bool and cannot be implicitly converted to one. You can call it to get a bool by passing an int. Change this line in the template:
TempCol.emplace_back(FalseVal(i));
then
auto OriginalElement = [&BoolList](int i) {return BoolList[i]; };
vector<bool> b = ConstructNestedVectorByElements(ElementIsZero, true, OriginalElement);
Compiles fine, but passing false will not work, because false is not a callable. T2 cannot be both, a callable and a bool. If you want both instantiations to work, you can change the first to
auto ElementIsZero = [&NumList](int i) {return NumList[i] == 0; };
vector<bool> a = ConstructNestedVectorByElements(ElementIsZero, true, [](int){ return false;});
Complete Demo
However, I suggest you to write two overloads. One that takes a callable. And to avoid the overhead of the callable, another one that takes a plain bool (no template parameter, let the conversion happen on the caller).
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)>{});
Experimenting with some templates and their uses:
I was working on this simple struct here:
template<typename T, size_t n> // templated but not variadic
struct myArray {
static const size_t SIZE = n;
T arr_[SIZE];
myArray() {} // Default Constructor
template<class... U> // Initialization Constructor
myArray( U pack... ) { // Templated with variadic parameters of
// type U = T upto SIZE = n;
arr_ = pack...;
}
};
I would like to use this in this manner or similar:
int main() {
myArray<char, 6> arr1{ 'a', 'b', 'c', 'd', 'e', 'f' };
myArray<int, 4> arr2{ 1, 2, 3, 4 };
// Or like this:
myArray<char, 6> arr1 = { 'a', 'b', 'c', 'd', 'e', 'f' };
myArray<int, 4> arr2 = { 1, 2, 3, 4 };
}
And in visual studio 2017RC I keep getting this compiler error:
1>------ Build started: Project: PracticeMath, Configuration: Debug Win32 ------
1>PracticeMath.cpp
1>c:\users\skilz80\documents\visual studio 2017\projects\practicemath\practicemath\practicemath.cpp(19): error C3520: 'U': parameter pack must be expanded in this context
1>c:\users\skilz80\documents\visual studio 2017\projects\practicemath\practicemath\practicemath.cpp(22): note: see reference to class template instantiation 'myArray<T,n>' being compiled
1>c:\users\skilz80\documents\visual studio 2017\projects\practicemath\practicemath\practicemath.cpp(41): fatal error C1903: unable to recover from previous error(s); stopping compilation
1>Done building project "PracticeMath.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Not sure what I am missing here or what to do within the initialization constructor.
A possible way to write that constructor is
template <class ... U>
myArray( U ... pack ) : arr_ { pack... }
{ }
Two errors in your code
(1) the ... is before the pack name in declaration; so
myArray ( U pack ... )
// ..............^^^ wrong
and
myArray ( U ... pack )
// .........^^^ correct
(2) there are a lot of modes to use a variadic pack but
arr_ = pack...;
isn't a correct one.
The usual way to use a variadic pack with a C-style array is in initialitation; that is, with constructor, in initialization list.
So
myArray( U ... pack ) : arr_ { pack... }
// .....................^^^^^^^^^^^^^^^^
But take in count that a constructor like this is dangerous because there isn't a check regarding the number of element of the pack (if greater than SIZE the behavior of the program is undefined (and this, usually, become: the program crash). A siple way to avoid this problem is add a static_assert() in the body of the constructor; something like
template <class ... U>
myArray( U ... pack ) : arr_ { pack... }
{ static_assert( sizeof...(U) <= N , "too much values" ); }
I'm trying to implement a simple N-dimensional array. This seems to be working more or less properly but I just can't get its overloaded ostream operator work. Here's my current implementation:
#include <iostream>
#include <memory>
#include <vector>
template <typename Type, int Dimension>
struct Array {
typedef std::vector<typename Array<Type, Dimension - 1>::type> type;
template <typename cType, int cDimension>
friend std::ostream &operator<<(std::ostream &stream, const Array<cType, cDimension>::type &object) {
if (cDimension == 0) {
stream << object << ' ';
} else {
typedef typename Array<cType, cDimension>::type::iterator ArrayIterator;
for (ArrayIterator it = object.begin(); it != object.end(); ++it) {
typedef typename Array<cType, cDimension - 1>::type NestedArray;
NestedArray nArray = (NestedArray)(*it);
stream << nArray << std::endl;
}
}
return stream;
}
};
template <typename Type>
struct Array < Type, 0 > {
typedef Type type;
};
int main() {
Array<int, 1>::type c00 = { 1, 2, 3 };
Array<int, 1>::type c01 = { 2, 3, 4 };
Array<int, 1>::type c02 = { 3, 4, 5 };
Array<int, 2>::type c10 = { c00, c01 };
Array<int, 2>::type c11 = { c01, c02 };
Array<int, 3>::type c20 = { c10, c11 };
std::cout << c20 << std::endl;
return 0;
}
I'm getting the following compilation errors:
1>------ Build started: Project: NDepthArray, Configuration: Debug Win32 ------
1> Source.cpp
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): warning C4346: 'Array<Type,Dimension>::type' : dependent name is not a type
1> prefix with 'typename' to indicate a type
1> c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(25) : see reference to class template instantiation 'Array<Type,Dimension>' being compiled
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): error C2061: syntax error : identifier 'type'
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): error C2805: binary 'operator <<' has too few parameters
1>c:\users\Administrator\documents\visual studio 2013\projects\cppmaterials\ndeptharray\source.cpp(10): fatal error C1903: unable to recover from previous error(s); stopping compilation
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
I literally tried all my ideas already, which includes removing friend keyword and the actual class parameter but nothing changes. How can we overload operators for such class templates?
Cheers,
Joey
The problem with your approach is that the cType cannot be inferred:
template <typename Type, int Dimension> // Asking for Type as cType
struct Array {
typedef std::vector<typename Array<Type, Dimension - 1>::type> type;
}
template <typename cType, int cDimension> ↓↓↓↓↓
std::ostream &operator<<(std::ostream &stream, typename Array<cType, cDimension>::type &object)
Array<int, 3>::type c20 = { c10, c11 };
std::cout << c20 // Deduction failure
You can find more info here: https://stackoverflow.com/a/12566268/1938163
14.8.2.5/4
In certain contexts, however, the value does not participate in type
deduction, but instead uses the values of template arguments that were
either deduced elsewhere or explicitly specified. If a template
parameter is used only in non-deduced contexts and is not explicitly
specified, template argument deduction fails.
As a sidenote: implementing complex structures for multidimensional arrays or vectors with templated recursive code is a not-very-maintainable and surely hard-to-read path to achieve something that could have been done faster, more efficient (less allocations) and clearer with only a contiguous block of memory indexed in different strides.
I am having following code which is taken from modern C++ design. While i am using it i am getting compiation error i think invalid sizeof opearand. Can any one point out what is the problem. Thanks!
template<bool>
struct CompileTimeChecker {
CompileTimeChecker(...);
};
template<>
struct CompileTimeChecker<false> {
};
#define STATIC_CHECK(expr, msg) \
{\
class ERROR_##msg {}; \
(void)sizeof(CompileTimeChecker<(expr) != 0>((ERROR_##msg())));\
}
template <class To, class From>
To safe_reinterpret_cast(From from) {
STATIC_CHECK(sizeof(From) <= sizeof(To), Destination_Type_Too_Narrow);
return reinterpret_cast<To>(from);
}
int main(void)
{
int a[20];
void* somePointer = a;
char c = safe_reinterpret_cast<int>(somePointer);
}
Error:
d:\technical\c++study\readparsing\readparsing\addressconv.cpp(29) : error C2066: cast to function type is illegal
1> d:\technical\c++study\readparsing\readparsing\addressconv.cpp(37) : see reference to function template instantiation 'To safe_reinterpret_cast(From)' being compiled
1> with
1> [
1> To=int,
1> From=void *
1> ]
1>d:\technical\c++study\readparsing\readparsing\addressconv.cpp(29) : error C2070: 'CompileTimeChecker<__formal> (safe_reinterpret_cast::ERROR_Destination_Type_Too_Narrow (__cdecl *)(void))': illegal sizeof operand
1> with
1> [
1> __formal=true
1> ]
Yet another strike for the most vexing parse...
sizeof(CompileTimeChecker<(expr) != 0>((ERROR_##msg()))
Is the same as
class Foo {};
class Bar {};
sizeof(Foo((Var()));
and as Foo(Var) can be interpreted either as a type (function taking a (function without an argument an returning a Var) and returning a Foo), it is so.
Like AProgrammer pointed out, the (void)sizeof isn't getting swallowed by the compiler. I suggest removing the parentheses from the sizeof, like this:
(void)sizeof CompileTimeChecker<(expr) != 0>((ERROR_##msg()));\
That seems to make g++ accept it, and interpret it the way it was probably meant to.
If that (void)sizeof keeps giving you trouble, you can get the static checking functionality without it too, for example by initializing a CompileTimeChecker variable:
CompileTimeChecker<(expr) != 0> a((ERROR_##msg()));\