C++ Alternating between two variables at compile time - c++

Suppose you have a class that operates on a vector:
class Foo{
public:
Foo() {
m_dynamic_data.push_back(5);
std::cout << m_dynamic_data[0] << std::endl;
}
private:
std::vector<int> m_dynamic_data;
};
In my case this class is huge with 2500 additional lines of code.
This class behaves dynamic (hence std::vector). But I would also like to provide a "static" implementation (using std::array). So std::size_t N is added, which now should control when to use which attribute.
template<std::size_t N>
class Foo{
private:
std::vector<int> m_dynamic_data; //use this, when N == 0
std::array<int, N> m_static_data; //use this, when N != 0
};
I am not sure if I can get this to work. using #define won't do the job (since it can't alternate). constexpr can't be wrapped around two attributes either. The best solution is probably to provide a base class and then inherit the dynamic and static case from it. But before I spent the next days doing this, I wonder if there isn't a technique afterall.
I thought about putting both into a std::unique_ptr and only constructing the relevant array:
template<std::size_t N>
class Foo {
public:
Foo() {
if constexpr (N) {
m_static_data_ptr = std::make_unique<std::array<int, N>>();
(*m_static_data_ptr)[0] = 5;
std::cout << (*m_static_data_ptr)[0] << std::endl;
}
else {
m_dynamic_data_ptr = std::make_unique<std::vector<int>>(1);
(*m_dynamic_data_ptr)[0] = 5;
std::cout << (*m_dynamic_data_ptr)[0] << std::endl;
}
}
private:
std::unique_ptr<std::vector<int>> m_dynamic_data_ptr;
std::unique_ptr<std::array<int, N>> m_static_data_ptr;
};
I earlier asked about this case here. But apparently this doesn't seem like a good approach. (fragmenting memory, cache miss rate). std::optional also seems interesting, but it pushes the sizeof(Foo) too far for my goal.
Ultimately there is also using void pointers:
template<std::size_t N>
class Foo {
public:
Foo() {
if constexpr (N) {
m_data = malloc(sizeof(std::array<int, N>));
(*static_cast<std::array<int, N>*>(m_data))[0] = 5;
std::cout << (*static_cast<std::array<int, N>*>(m_data))[0] << std::endl;
}
else {
m_data = new std::vector<int>;
(*static_cast<std::vector<int>*>(m_data)).push_back(5);
std::cout << (*static_cast<std::vector<int>*>(m_data))[0] << std::endl;
}
}
~Foo() {
delete[] m_data;
}
private:
void* m_data;
};
But this seems pretty dirty [...]
So the goal would be to work with either array structure at compile time. Thanks for any help / suggestion!

You can abstract the data part of Foo to another class template.
template<std::size_t N> struct FooData
{
std::array<int, N> container;
}
template <> struct FooData<0>
{
std::vector<int> container;
}
template<std::size_t N>
class Foo{
private:
using DataType = FooData<N>;
DataType data;
};
You have to add member functions to FooData to support additional abstractions. The number of such functions and their interface depends on how differently you use the containers in Foo.

R Sahu's answer is great, but you don't need to access the container indirectly through a struct.
template<std::size_t N>
struct FooData { using type = std::array<int, N>;};
template <>
struct FooData<0> { using type = std::vector<int>; };
template<std::size_t N>
using FooData_t = typename FooData<N>::type;
template<std::size_t N>
class Foo{
private:
FooData_t<N> data;
};
Alternatively, you can also use std::conditional_t:
template<std::size_t N>
class Foo{
private:
std::conditional_t<N==0, std::vector<int>, std::array<int, N>> data;
};

You may want to isolate this dynamic/static "morphing" from the rest of your giant 2500-lines Foo class. I can imagine a tiny wrapper around std::array to mimic the interface of std::vector. It can be used as a member of Foo. If the static capacity is set to the sentinel value 0, then it can be specialized to just derive from a real std::vector:
#include <cassert>
#include <cstddef>
#include <array>
#include <iostream>
#include <vector>
template<class value_type_, std::size_t capacity_>
struct StaticOrDynamic {
using value_type = value_type_;
static constexpr std::size_t capacity = capacity_;
std::array<value_type, capacity> arr_{};
std::size_t size_{0};
constexpr void push_back(const value_type& x) {
assert(size_ < capacity && "must not exceed capacity");
arr_[size_++] = x;
}
constexpr const value_type_& at(std::size_t i) const {
assert(i < size_ && "must be in [0, size)");
return arr_[i];
}
/* other members etc */
};
template<class value_type_>
struct StaticOrDynamic<value_type_, 0>// specialization for dynamic case
: std::vector<value_type_>
{
using std::vector<value_type_>::vector;
};
template<std::size_t capacity_>
struct Foo {
static constexpr std::size_t capacity = capacity_;
StaticOrDynamic<int, capacity> m_data_{};
Foo() {// static version may be constexpr (without debug output)
m_data_.push_back(5);
std::cout << m_data_.at(0) << std::endl;
}
};
int main() {
Foo<5> static_foo{};
Foo<0> dynamic_foo{};
}
A similar behavior (static/dynamic chosen by a template parameter) is offered in, e.g., the Eigen library. I do not know how it is implemented there.

Related

How to build an efficient named array?

I want to have a type that both has named members and also is iterable, so one should be able to refer to the members by index, by label or from a for-each loop. One way to realise this would be to use std::unordered_map<std::string,T> with some helper data for the indices. Clearly this would be horribly inefficient as for each member access you need to hash a std::string.
My current attempt looks like this:
// named-array.h
#pragma once
#include <array>
#include <cstddef>
#define NamedArray_KeyDecl(Name, ...) enum class Name : std::size_t { __VA_ARGS__, NUM }
namespace util {
template <typename K, typename T>
struct NamedArray {
static constexpr std::size_t cast(K k) {
return static_cast<std::size_t>(k);
}
std::array<T,cast(K::NUM)> array;
NamedArray(std::array<T,cast(K::NUM)> a) : array(a) {
}
constexpr T& operator[](K k) {
return array[cast(k)];
}
constexpr T const& operator[](K k) const {
return array[cast(k)];
}
};
}
Which can be used like so:
struct Gadget {
int i;
Gadget(int i) : i(i) {}
void operator()() const {
std::cout << "Gadget(" << i << ")\n";
}
};
NamedArray_KeyDecl(Test1, a,b,c,d);
util::NamedArray<Test1,Gadget> gadgets {{0,1,2,3}};
// for each works:
for (auto const& gadget: gadgets.array) {
gadget();
}
// named access works:
gadgets[Test1::b]();
// access by index works:
gadgets.array[1]();
Exposing the array member could be avoided by forwarding all interface functions of std::array.
However, an obvious drawback is that
gadgets[Test1::b] is not as pretty as something along the lines of gadgets.member().b and
there is an exposed #define in a c++ header file (which is extremely smelly)
Is there a way to have a named array with the same performance as an std::array?
Minimalistic example of how it could be done:
#include <array>
#include <type_traits>
template<class Tag, class...Tags>
struct position {
};
template<class Tag, class...Tags>
struct position<Tag, Tag, Tags...> {
constexpr static unsigned value = 0;
};
template<class Tag, class First, class...Tags>
struct position<Tag, First, Tags...> {
constexpr static unsigned value = 1 + position<Tag, Tags...>::value;
};
template<class T, class...Tags>
class NamedArray {
public:
template<class U>
constexpr T& operator[](U tag) {
return array_[position<U, Tags...>::value];
}
constexpr T& operator[](unsigned val) {
return array_[val];
}
template<class U>
constexpr T& member(U u = U{}) {
return (*this)[u];
}
private:
std::array<T, sizeof...(Tags)> array_;
};
struct tag1{};
struct tag2{};
int main() {
NamedArray<int, tag1, tag2> a;
a[tag1{}];
a[tag2{}];
a.member(tag1{});
a.member<tag1>();
}
Just define the enums as you want them. And leaving them unscoped is fine, you want the names to leak into the declaration's scope, and are helped by the implicit conversion to std::size_t.
template <typename K, typename T>
using NamedArray = std::array<T, K::NUM>;
Then
struct Gadget {
int i;
Gadget(int i) : i(i) {}
void operator()() const {
std::cout << "Gadget(" << i << ")\n";
}
};
enum Test1 : std::size_t { a, b, c, d, NUM };
int main() {
NamedArray<Test1,Gadget> gadgets { 0,1,2,3 };
// for each works:
for (auto const& gadget: gadgets) {
gadget();
}
// named access works:
gadgets[b]();
// access by index works:
gadgets[1]();
}

Construct std::array and initialize element objects via code

I want to initialize my array items while avoiding unnecessary instances and copies (similar to this question: initialize std::array without copying/moving elements).
An initializer list does work for a small amount of objects.
Bit I want to do this via a code snippet since my array has several hundreds of items...
How can I do this?
#include <array>
#include <iostream>
class mytype {
public:
int a;
mytype() : a(0) {}
mytype(int a) : a(a) {}
};
int main() {
// explict constructor calls to instantiate objects does work
std::array<mytype, 2> a = { { mytype(10), mytype(20) } };
std::cout << a[0].a; // 10
// I want to do something like this - what does not work of course
std::array<mytype, 2> b = { { for (i = 0, i++, i < 2) mtype(10 * i); } };
}
In c++14:
#include <array>
#include <utility>
#include <cstddef>
template <typename T, std::size_t... Is>
std::array<T, sizeof...(Is)> to_array(std::index_sequence<Is...>)
{
return { T(Is*10)... };
}
template <typename T, std::size_t N>
std::array<T, N> to_array()
{
return to_array<T>(std::make_index_sequence<N>{});
}
int main()
{
std::array<mytype, 10> b(to_array<mytype, 10>());
}
DEMO
This is typically accomplished with a pair of templates:
namespace detail {
template<std::size_t... Idx>
auto make_mytype_array(std::index_sequence<Idx...>) {
return std::array<mytype, sizeof...(Idx)>{{
mytype(10 * Idx)...
}};
}
}
template<std::size_t N>
auto make_mytype_array() {
return detail::make_mytype_array(make_index_sequence<N>{});
}
The above are a pair of utility free functions, but can be folded into the class if need be. If you need it for more than just an expression like 10*i, then a lambda can be passed as another argument (templated to be a general "callable"). With copy elision this will all collapse into direct initialization of the result array object.

Compile time creation of class member stl container (const std::array) filled with elements

I tried to create a minimal example as it is possible with templates.
(MSVC15, c++14)
There are 2 questions may be connected with each other.
Question 1: Is it possible to create at compile time container class member (for example std::array) filled with elements?
_limited_int_ptr_container _intIndexes
{
// _startIntIndex
// create indexes???
std::make_shared<_limited_int_>(_startIntIndex), // 10060u _startStrIndex
//std::make_shared<_limited_int_>(_startIntIndex + 1),
//std::make_shared<_limited_int_>(_startIntIndex + 2),
//...
std::make_shared<_limited_int_>(_startIntIndex + _maxIntIndexes -1)
};
Question 2: Is it possible to combine several template classes with one general template parameter but only once?
template <class T, size_t Size>
using _container = const std::array<T, Size>;
template <class T>
using _ptr = std::shared_ptr<T>;
// is it possible to combine this 2 types and use _maxStrIndexes only once?
using _limited_str_ = IndexStr<_maxStrIndexes>;
using _limited_str_ptr = _ptr<_limited_str_>;
using _limited_str_ptr_container = _container<_limited_str_ptr, _maxStrIndexes>;
Full code:
#include <iostream>
#include <memory>
#include <array>
template<class T, size_t MaxIndex>
class BaseIndex
{
public:
virtual ~BaseIndex() = default;
explicit BaseIndex(const size_t index)
: _maxIndex(MaxIndex), _currentIndex(index) {}
virtual void DoSmth() = 0;
friend std::ostream & operator << (std::ostream & ss, BaseIndex & base_index)
{
ss << "Max: " << base_index._maxIndex << ". Current: " << base_index._currentIndex << std::endl;
return ss;
}
protected:
const size_t _maxIndex;
size_t _currentIndex{ 0u };
};
template<size_t MaxIndex>
class IndexStr : public BaseIndex<std::string, MaxIndex>
{
public:
explicit IndexStr(const size_t index) :BaseIndex(index) {}
void DoSmth() override { std::cout << IndexStr::_maxIndex; }
};
template<size_t MaxIndex>
class IndexInt :public BaseIndex<int, MaxIndex>
{
public:
explicit IndexInt(const size_t index) :BaseIndex(index) {}
void DoSmth() override { std::cout << IndexInt::_maxIndex; }
};
class Manager
{
private:
static const size_t _startStrIndex{ 11u };
static const size_t _startIntIndex{ 10060u };
static const size_t _maxStrIndexes{ 5 };
static const size_t _maxIntIndexes{ 120 };
public:
template <class T, size_t Size>
using _container = const std::array<T, Size>;
template <class T>
using _ptr = std::shared_ptr<T>;
// is it possible to combine this 2 types and use _maxStrIndexes only once?
using _limited_str_ = IndexStr<_maxStrIndexes>;
using _limited_str_ptr = _ptr<_limited_str_>;
using _limited_str_ptr_container = _container<_limited_str_ptr, _maxStrIndexes>;
// const std::array<std::shared_ptr<IndexStr<_maxStrIndexes>>, _maxStrIndexes> _strIndexes
_limited_str_ptr_container _strIndexes
{
// _startStrIndex
// create indexes ???
// std::integer_sequence ???
std::make_shared<_limited_str_>(_startStrIndex), // 11 = _startStrIndex
std::make_shared<_limited_str_>(12),
std::make_shared<_limited_str_>(13),
std::make_shared<_limited_str_>(14),
std::make_shared<_limited_str_>(_startStrIndex + _maxStrIndexes - 1)
};
// is it possible to combine this 2 types and use _maxIntIndexes only once?
using _limited_int_ = IndexInt<_maxIntIndexes>;
using _limited_int_ptr = _ptr<_limited_int_>;
using _limited_int_ptr_container = _container<_limited_int_ptr, _maxIntIndexes>;
_limited_int_ptr_container _intIndexes
{
// _startIntIndex
// create indexes???
std::make_shared<_limited_int_>(_startIntIndex), // 10060u _startStrIndex
//...
std::make_shared<_limited_int_>(_startIntIndex + _maxIntIndexes -1)
};
};
template <class T, size_t Size >
void Process(const std::array<std::shared_ptr<T>, Size> _array)
{
for (auto &&baseindex : _array)
std::cout << *baseindex;
}
int main()
{
Manager m;
Process(m._strIndexes);
//Process(m._intIndexes);
return 0;
}
Live demo
About the first question: yes, it is possible. It is even quite easy provided that you can use C++11 and variadic templates.
To generate a compile-time list of indices, you can use std::make_index_sequence<N>, which will return a std::index_sequence<0, 1, 2, 3, ..., N-1>. You can then pattern-match it with the function creating the compile-time array:
template <std::size_t... Ns>
constexpr auto fill_it_at_compile_time_impl(std::index_sequence<Ns...>) {
return std::array<unsigned, sizeof...(Ns)>{ Ns... };
}
template <std::size_t N>
constexpr auto fill_it_at_compile_time() {
return fill_it_at_compile_time_impl(std::make_index_sequence<N>());
}
If you want to add an offset to the index_sequence members, just do so in the array initialization:
constexpr auto offset = 10u;
template <std::size_t... Ns>
constexpr auto fill_it_at_compile_time_impl(std::index_sequence<Ns...>) {
return std::array<unsigned, sizeof...(Ns)>{ (offset+Ns)... };
}
About the second question:
As far as I understand it, yes, it's possible. First, create a helper struct to query the index of _limited_str:
template <typename T>
struct query_max_index;
template <std::size_t N>
struct query_max_index<IndexStr<N>> {
static const auto max_index = N;
};
Then, rather than refering to _maxStrIndexes directly, you can query it from indirectly from _limited_str_ptr:
using _limited_str_ = IndexStr<_maxStrIndexes>;
using _limited_str_ptr = _ptr<_limited_str_>;
using _limited_str_ptr_container = _container<_limited_str_ptr, query_max_index<std::decay_t<decltype(*std::declval<_limited_str_ptr>())>>::max_index>;

Passing a default value list to a template class

Not sure if this can be done using templates but I want to give it a try.
I have a template class which takes any struct, stores it and returns it. Additionally, I want an interface that resets the struct's data whenever requested.
#define MYDEFAULT {1,2,3}
template < typename ITEM, ITEM Default>
class myClass{
public:
myClass(ITEM item) : _item(item) {}
const ITEM* get(){
return &_item;
}
void reset(){
_item = Default;
}
ITEM _item;
};
// Set to default when instantiated
myClass<myStruct, MYDEFAULT> ABC(MYDEFAULT);
Of course that's not working at all, but what I want to achieve is the replacement of Default in reset(). I mean it would work if _item would be of type int.
How can this be realized?
EDIT: I want something like this:
template <typename Y, Y T>
class myclass {
public:
void reset() {
xxx = T;
}
Y xxx{10};
};
void test()
{
myclass<int, 5> _myclass;
}
Initially xxx is 10 and after invoking reset it is 5. This works, so it seems it is not possible for POD or class types?
EDIT2: It seems it is all about non-type template-arguments. https://stackoverflow.com/a/2183121/221226
So there is no way around traits when using structs.
As a viable solution, you can use a trait class as shown in the following working example:
#include<cassert>
struct S {
int i;
};
template<typename T>
struct Traits {
static constexpr auto def() { return T{}; }
};
template<>
struct Traits<S> {
static constexpr auto def() { return S{42}; }
};
template <typename ITEM>
class myClass {
public:
myClass(): _item(Traits<ITEM>::def()) {}
myClass(ITEM item): _item(item) {}
const ITEM* get() {
return &_item;
}
void reset() {
_item = Traits<ITEM>::def();
}
ITEM _item;
};
int main() {
myClass<S> ABC{};
myClass<int> is;
assert((ABC.get()->i == 42));
assert((*is.get() == 0));
}
The basic trait uses the default constructor of the type ITEM.
You can then specialize it whenever you want a different defaulted value for a specific class.
The same can be accomplished even with a factory function as:
template<typename T>
constexpr auto def() { return T{}; }
template<>
constexpr auto def<S>() { return S{42}; }
Anyway, traits can easily provide more types and functions all at once.
You can maybe simulate it using a data structure with a data member of type std::array.
A minimal, working example follows:
#include<cstddef>
#include<array>
#include<cassert>
template<typename T, T... I>
struct S {
S(): arr{ I... } {}
S(const T (&val)[sizeof...(I)]) {
for(std::size_t i = 0; i < sizeof...(I); ++i) {
arr[i] = val[i];
}
}
const T * get() {
return arr.data();
}
void reset() {
arr = { I... };
}
private:
std::array<T, sizeof...(I)> arr;
};
int main() {
S<int, 1, 3, 5> s{{ 0, 1, 2 }};
assert(s.get()[1] == 1);
s.reset();
assert(s.get()[1] == 3);
}
I'm not sure I got exactly what you are asking for, but the interface in the example is close to the one in the question and the implementation details should not affect the users of your class.

Best Way to Create N Objects at Compile Time

I wondered how you would go about creating N objects at compile time with templates. Or this was indeed even good practise.
I have a header file containing some constants:
constexpr size_t N_TIMES = 3;
constexpr uint32_t MIN[N_TIMES] = {0,1,2};
constexpr uint32_t MAX[N_TIMES] = {1,2,3};
Then a header file containing a template which will be generate "N" times:
template <typename T>
class foo
{
public:
foo(uint32_t min , uint32_t max) :
min(min),
max(max)
{
std::cout << "I was created with " << min << " " << max << std::endl;
}
private:
const uint32_t min;
const uint32_t max;
};
The part I'm a little unsure about is I have:
template <typename T>
class bar
{
public:
bar()
{
for(auto i = 0; i < N_TIMES; i ++)
{
foo_[i] = foo<T>(MIN[i], MAX[i]);
}
}
private:
std::array<foo<T>, N_TIMES> foo_;
};
I currently get the error:
cannot be assigned because its copy assignment operator is implicitly deleted
But since it's in the constructor, it'll generate this after compiling anyway. So really I was just wondering how I should be going about this instead. If there was some kind of clever recursive trick I could pull to create these objects for me at compile time.
You may use std::index_sequence:
namespace detail
{
template <typename T, std::size_t N, std::size_t...Is>
std::array<Foo<T>, N> make_foo_array(std::index_sequence<Is...>)
{
return {{Foo<T>(MIN[Is], MAX[Is])...}};
}
}
template <typename T, std::size_t N>
std::array<Foo<T>, N> make_foo_array()
{
return detail::make_foo_array<T, N>(std::make_index_sequence<N>{});
}
And then
template <typename T>
class bar
{
public:
bar() : foo_(make_foo_array<T, N_TIMES>()) {}
private:
std::array<foo<T>, N_TIMES> foo_;
};