Reputation: 11
I have large enum for example:
enum { elem0, elem1, elem2, elem3 ...... elem1000 }
I would like to create an array of float
numbers using just some of the enum elements.
For example, I would like to have only three elements in array: elem0
, elem7
, elem999
and I would like to acccess them in similar style as this:
array[elem0]=123
array[elem7]=12.5
array[elem999]=15.6
What would be the most elegant way to implement such an array so that array is going to have only three elements ?
Upvotes: 1
Views: 932
Reputation: 69882
Maybe overkill, but then again maybe not - template expansion can write conversion functions for you.
This template class allows you to specify which elems should be valid for the vector, and in which order.
It also provides the ability to index into the vector by elem, either dynamically (throw exception if does not exists) or statically (fail to compile if out of bounds).
#include <array>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <stdexcept>
//
// boilerplate
//
namespace notstd {
template<class T, class Tuple>
struct tuple_index;
template<class T, class... Types>
struct tuple_index<T, std::tuple<T, Types...>> {
using type = std::size_t;
static const type value = 0;
};
template<class T, class U, class... Types>
struct tuple_index<T, std::tuple<U, Types...>> {
using type = std::size_t;
static const type value = 1 + tuple_index<T, std::tuple<Types...>>::value;
};
}
enum element_type {
elem0, elem1, elem2, elem3, elem4, elem5, elem1000
};
template<element_type Value>
struct where_type
{
static const element_type value = Value;
};
template<element_type Value>
static constexpr auto where = where_type<Value> {};
template<element_type...Permitted>
struct restricted_vector : private std::array<double, sizeof...(Permitted)> {
using corresponding_tuple = std::tuple<where_type<Permitted>...>;
using inherited = std::array<double, sizeof...(Permitted)>;
using inherited::inherited;
static auto conversion(element_type e) -> std::size_t
{
static const std::array<std::pair<element_type, std::size_t>, sizeof...(Permitted)> a = {
std::make_pair(Permitted, notstd::tuple_index<where_type<Permitted>, corresponding_tuple>::value)...
};
auto ifind = std::find_if(a.begin(), a.end(), [e](auto&& elem) { return elem.first == e; });
if (ifind == a.end()) {
throw std::out_of_range("invalid element");
}
return ifind->second;
}
template<element_type Elem>
auto& operator[](where_type<Elem>) {
auto pos = notstd::tuple_index<where_type<Elem>, corresponding_tuple >::value;
return inherited::operator[](pos);
}
template<element_type Elem>
auto const& operator[](where_type<Elem>) const {
auto pos = notstd::tuple_index<where_type<Elem>, corresponding_tuple >::value;
return inherited::operator[](pos);
}
// dynamic access
auto& at(element_type e) {
return inherited::operator[](conversion(e));
}
auto const& at(element_type e) const {
return inherited::operator[](conversion(e));
}
using inherited::begin;
using inherited::end;
using inherited::size; // etc
};
int main() {
auto v1 = restricted_vector<elem3, elem4, elem5> {};
v1[where<elem4>] = 0.4;
v1[where<elem5>] = 0.5;
v1[where<elem3>] = 0.3;
std::copy(v1.begin(), v1.end(), std::ostream_iterator<double>(std::cout, ", "));
std::cout << "\n";
std::cout << "at elem4: " << v1.at(elem4) << std::endl;
}
expected output:
0.3, 0.4, 0.5,
at elem4: 0.4
Upvotes: 0
Reputation: 11761
Just write a conversion from enum to array index:
int EnumToIdx(Elem elem)
{
switch (elem)
{
case elem0: return 0;
case elem7: return 1;
case elem999: return 2;
}
throw std::invalid_argument("EnumToIdx: no conversion"); // or whatever
}
Usage
array[EnumToIdx(elem0)] = 123;
array[EnumToIdx(elem7)] = 12.5;
array[EnumToIdx(elem999)] = 15.6;
Upvotes: 3