C++ const std::array size from constructor - c++

Say I get an int from a lambda function ran at initialization of a class object. Is it possible to use that int to define the size of a std::array? Something like the following code.
#include <array>
#include <vector>
#include <iostream>
class Test1 {
public:
Test1( std::vector<int> vec1 ) :
nvars([&vec1]() -> int { return vec1.size()+1; }())
{
};
const int nvars;
// ******This isn't allowed!!!!!!!!!
const std::array<int,nvars> arr;
};
int main() {
std::vector<int> vec{1,2,3,4};
Test1 test1(vec);
std::cout << "nvars: " << test1.nvars << std::endl;
return 0;
}
I am a C++ beginner so any other advice will be welcome.

No. The size of the array is part of its type. You cannot let it be determined at runtime.
You can have it be determined at compile time, if you do pass a std::array to the constructor. Since C++17 there is CTAD (class template argument deduction) which lets you write:
#include <array>
template <size_t N>
class Test1 {
public:
Test1( std::array<int,N> vec1 ) :
arr(vec1)
{
};
const std::array<int,N> arr;
};
int main() {
std::array vec{1,2,3,4};
Test1 test1(vec);
}
Live Demo
test1 is of type Test1<4>. Note that Test1<4> is a distinct different type than eg Test<5> or Test<24>. If you want one type to have a member array of different size, make it a std::vector.

Related

Conditional compilation (constexpr if) and "ISO C++ forbids zero-size array"

With the following code:
#include <algorithm>
constexpr int DATA_SIZE = 5;
constexpr int A_ARRAY_ALLOWED_SIZE = 5;
constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE);
constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE;
class A {
int a[A_ARRAY_SIZE];
};
class B {
int b[B_ARRAY_SIZE];
};
int main()
{
A a;
if constexpr (B_ARRAY_SIZE)
{
B b;
}
return 0;
}
I'm getting compiler error (with -pedantic flag) which complains that zero-size array is not allowed. In my example the object with the zero size array is never created but looks like it is still an issue.
I was trying to workaround it with usage of std::conditional but even then I ended up with an additional function like:
constexpr int Get_B_ARRAY_SIZE()
{
if (B_ARRAY_SIZE)
return B_ARRAY_SIZE;
return 1; // workaround for zero-size array
}
What is a proper way of handling such an issue?
EDIT:
I'm aware that all of if branches should contain valid code. I'm also aware that zero-size arrays are not allowed. My question is how to refactor this code to get similar behawior like when compiling without -pedantic flag. I suspect that I can use template meta programming to achieve this purpose but I'm not sure how to do it.
If you need equivalent of std::conditional, but for values, rather than types, you can do it like this:
#include <iostream>
#include <type_traits>
template<size_t N>
struct safe_array_size : std::integral_constant<size_t, N> {};
template<>
struct safe_array_size<0> : std::integral_constant<size_t, 1> {};
int main()
{
char a[safe_array_size<0>::value];
char b[safe_array_size<1>::value];
std::cout << sizeof(a) << std::endl;
std::cout << sizeof(b) << std::endl;
}
Or using std::conditional:
#include <iostream>
#include <type_traits>
template<size_t N>
constexpr size_t safe_array_size = std::conditional_t<N==0, std::integral_constant<size_t, 1>, std::integral_constant<size_t, N>>::value;
int main()
{
char a[safe_array_size<0>];
char b[safe_array_size<1>];
std::cout << sizeof(a) << std::endl;
std::cout << sizeof(b) << std::endl;
}
if constexpr (at least, how you are using it) cannot directly work around this error because it is the class definition that is ill-formed. Whether or not you instantiate the class is irrelevant. You can fix this by ensuring that the array size is never zero with std::max:
#include <algorithm>
constexpr int DATA_SIZE = 5;
constexpr int A_ARRAY_ALLOWED_SIZE = 5;
constexpr int A_ARRAY_SIZE = std::min(A_ARRAY_ALLOWED_SIZE, DATA_SIZE);
constexpr int B_ARRAY_SIZE = DATA_SIZE - A_ARRAY_ALLOWED_SIZE;
class A {
int a[A_ARRAY_SIZE];
};
class B {
int b[std::max(B_ARRAY_SIZE, 1)];
};
int main()
{
A a;
if constexpr (B_ARRAY_SIZE)
{
B b;
}
return 0;
}
Note that std::max is constexpr as of C++14. You could implement your own max function if you are on C++11.
If you need to ensure that the class is never actually instantiated unless the array size is non-zero, the if constexpr check in the above code will handle that.

Use of incomplete types with std::variant

Suppose I have next code, that is used for simply storing reference/pointer to objects of types A, B or C. I actually don't need complete types.
Now i have the following solution, where I need a lot of #include bloat.
Header:
using MyVariant = std::variant<class A, class B, class C, ...>;
class Holder {
public:
Holder(MyVariant &&TheValue);
const MyVariant &GetValue();
private:
std::unique_ptr<MyVariant> Value;
};
Source file:
#include "A.hpp"
#include "B.hpp"
#include "C.hpp"
Holder::Holder(MyVariant &&TheValue)
: Value(std::make_unique<MyVariant>(std::move(TheValue)) {}
const MyVariant &Holder::GetValue { return Value; }
How I can implement the same semantics without all instantiated types as std::variant template parameters / dynamic memory allocation / dynamic polymorphism?
As others have pointed out the specifications seem a little contradicting.
I can only think of variable template arguments, to achieve not having to specify all possible classes for the Holder class.
Example
#include <iostream>
#include <memory>
#include <variant>
// h
template<typename ...T>
class Holder {
using variant = std::variant<T...>;
public:
Holder(variant &&TheValue)
:Value(std::make_unique<variant>(std::move(TheValue)))
{
};
template<typename TARGET>
const TARGET &GetValue() const {
return std::get<TARGET>(*Value);
};
private:
std::unique_ptr<variant> Value;
};
// main.cpp testing
int main() {
auto a = Holder<int, float>(5);
const auto v = a.GetValue<int>();
std::cout << "holding int: " << v << std::endl;
a = Holder<int, float>(5.5f);
const auto v2 = a.GetValue<float>();
std::cout << "holding float: " << v2 << std::endl;
}
I think at that point it looks like a rather redundant class though.

How to Initialize a vector<typeinfo>?

I am able to do this:
std::vector<int> vec = { 1, 2, 3, 4, 5 };
But I am not able to do this:
std::vector<const type_info&> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says pointer to reference is illegal
or
std::vector<const type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
compiler says Error C2338 The C++ Standard forbids containers of const elements because allocator is ill-formed.
or
std::vector<type_info> ClassBlackList = { typeid(Class1), typeid(Class2) };
Compiler says:
Error C2280 'type_info::type_info(const type_info &)': attempting to reference a deleted function
I am able not able to do push_back either.
What is the solution to have a vector or list of type_info?
You can use pointers
std::vector<const std::type_info*> v = { &typeid(Class1), &typeid(Class2) };
This is valid because typeid returns a reference to an object with static storage duration.
You cannot have a vector of references, for several fundamental reasons. C++ simply does not work this way. You can, however, employ std::reference_wrapper to get pretty much the same result:
#include <functional>
#include <vector>
#include <typeinfo>
class A {
};
int main()
{
std::vector<std::reference_wrapper<const std::type_info>> avec;
auto &t=typeid(A);
avec.push_back(t);
const std::type_info &i=avec[0];
return 0;
}
You can't have arrays of references so you could wrap them in std::reference_wrappers:
#include <functional>
#include <typeinfo>
#include <vector>
std::vector<std::reference_wrapper<const std::type_info>> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
The name ClassBlackList implies that you will search it a lot and also that the elements in the list are to be unique. In that case, you may want to use a std::set instead.
Example:
#include <functional>
#include <iostream>
#include <typeinfo>
#include <set>
struct Class1 {};
struct Class2 {};
struct Class3 {};
struct comp { // a functor to compare reference wrapped type_info's
std::size_t operator()(const std::reference_wrapper<const std::type_info>& lhs,
const std::reference_wrapper<const std::type_info>& rhs) const
{
return std::less<const std::type_info*>{}(&lhs.get(), &rhs.get());
}
};
int main() {
std::set<std::reference_wrapper<const std::type_info>, comp> ClassBlackList = {
typeid(Class1),
typeid(Class2)
};
// try to insert typeid(Class3) twice, it only succeeds the first time
auto[it1, inserted1] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted1 << '\n';
auto[it2, inserted2] = ClassBlackList.insert(typeid(Class3));
std::cout << "inserted: " << inserted2 << '\n';
}
Output:
inserted: 1
inserted: 0

How to copy elements from std::list to an array of struct?

I need to copy the contents of a std::list into an array, wherein the array is struct of array. Below is the code implementation of it.
#include <iostream>
#include <string>
using namespace std;
typedef struct
{
int height;
int width;
int length;
}dimensions;
GetDimensions(list<std::string>, *int); // Function that copies the content of list to array passed as second parameter
int main()
{
dimensions cuboid[10];
int plane[10];
list<std::string> planeList = GetList();//Function that returns list of elements
list<std::string> dimensionList = GetList();
GetDimensions(planeList,&plane);//This is fine, as it is a simple array
GetDimensions(dimensionList,&cuboid.height);//Trouble in implementation of this usecase, for cuboid.height, cuboid.width and cuboid.height.
return 0;
}
GetDimensions(list<std::string>dimensionList, int* dimensionParams)
{
int i=0;
for(list<std::string>::iterator it = dimensionList.begin(); it != dimensionList.end(); ++it)
{
dimensionParams[i] = stoi(*it);
i++;
}
}
Here, I need GetDimensions() function to copy the list (passed as first parameter) to array (second parameter). The implemented function works well for simple array plane. But how to pass the array of struct as parameter to the function ?
I will be getting the std::list as cuboid.height, cuboid.width and cuboid.length. So the function has to copy the contents of list from cuboid[0].height to cuboid[i].height respectively. Is there any specific function to copy the content directly?
Use std::array 's instead. Then your problem can be reduced to passing two different types of arrays to a single function.
This can be solved
either by good old function overloads
or in c++17 function template with
if-constexpr.
Following is an example code with templated function with if-constexpr (See live online)
#include <iostream>
#include <string>
#include <list>
#include <array>
#include <type_traits> // std::is_same_v
struct dimensions // no need to typedef here
{
int height;
int width;
int length;
};
template<typename T>
void GetDimensions(const list<std::string>& dimensionList, T& dimensionParams)
^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //---> pass list by const-ref as the values are non-modifying
{
int i{0};
if constexpr (std::is_same_v<std::array<int, 10>, T>)
{
for(const std::string& str: dimensionList) dimensionParams[i++] = std::stoi(str);
}
else
{
for(const std::string& str: dimensionList) dimensionParams[i++].height = std::stoi(str);
}
}
int main()
{
std::array<dimensions, 10> cuboid; // use std::array instead of VLA
std::array<int, 10> plane;
std::list<std::string> planeList{"1", "2"}; // some list
std::list<std::string> dimensionList{"1", "2"};
GetDimensions(planeList, plane);
GetDimensions(dimensionList, cuboid);
return 0;
}
Also note that:
You have not specified the return type of GetDimensions function.
You probably want to return void there.
in C++ you do not need to use typedef alias for struct { ... }.
last but not least, do not practice with using namespace std;
You can do this with boost::transform_iterator.
#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
struct dimensions {
int height;
int width;
int length;
};
template <typename OutputIt>
void GetDimensions(std::list<std::string> dimensionList, OutputIt dimensionParams)
{
// N.b. taking the address of a standard library function is undefined, so wrap in a lambda
auto stoi = [](std::string s){ return std::stoi(s); };
std::copy(boost::make_transform_iterator(dimensionList.begin(), stoi),
boost::make_transform_iterator(dimensionList.end(), stoi),
dimensionParams);
}
int main() {
dimensions cuboid[10];
int plane[10];
std::list<std::string> planeList = GetList();
std::list<std::string> heightList = GetList();
std::list<std::string> widthList = GetList();
std::list<std::string> lengthList = GetList();
GetDimensions(planeList, plane);
GetDimensions(heightList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::height)));
GetDimensions(widthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::width)));
GetDimensions(lengthList,
boost::make_transform_iterator(cuboid, std::mem_fn(&dimensions::length)));
return 0;
}

Boost Hana : Convert Hana Types to std::string's

Does there exist a Boost Hana method for compile-time converting the types of members of a Struct concept to a STL container of std::string's of the typenames?
For example,
MyType t();
std::array<std::string, 3> ls = boost::hana::typesToString(t);
for(std::string x : ls){
std::cout << x << std::endl;
}
Yields "int string bool" to STDOUT,
With
class MyType{
int x;
std::string y;
bool z;
}
The documentation clearly provides methods for getting the members and their values of an instance of a Struct concept, but I haven't found anything there that does this for the types of the members. A simpler task would be to do:
int x;
std::string tName = boost::hana::typeId(x); //tName has value "int"
I've read this post but I'd like to know if there's a clean way out-of-the-box in Hana. Even better would be a way to iterate through the members of the Struct without having to know them by name.
If you are using Clang, Hana has an experimental feature hana::experimental::type_name. This can be used to get the type-names of the members of the struct:
#include <boost/hana.hpp>
#include <boost/hana/experimental/type_name.hpp>
namespace hana = boost::hana;
template <typename Struct>
auto member_type_names() {
constexpr auto accessors = hana::accessors<Struct>();
return hana::transform(
accessors,
hana::compose(
[](auto get) {
using member_type
= std::decay_t<decltype(get(std::declval<Struct>()))>;
return hana::experimental::type_name<member_type>();
},
hana::second
)
);
}
Demo (live on Wandbox):
#include <iostream>
#include <string>
struct MyType {
int a;
std::string b;
float c;
};
BOOST_HANA_ADAPT_STRUCT(MyType, a, b, c);
int main() {
hana::for_each(member_type_names<MyType>(), [](auto name) {
// Note that the type of `name` is a hana::string, not a std::string
std::cout << name.c_str() << '\n';
});
}
Outputs:
int
std::__1::basic_string<char>
float