1

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 ?

10
  • And your question is? Commented Mar 24, 2017 at 18:32
  • What would be the most elegant way to implement such an array so that array is going to have only three elements. Commented Mar 24, 2017 at 18:33
  • Possible duplicate of Using an enum as an array index Commented Mar 24, 2017 at 18:35
  • 2
    @Egon Did you consider std::map? Commented Mar 24, 2017 at 18:36
  • 1
    @Egon Please elaborate: Why is std::map too heavy for your use case? Commented Mar 24, 2017 at 18:53

2 Answers 2

3

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;
Sign up to request clarification or add additional context in comments.

Comments

0

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

2 Comments

Now we know who is to blame personally when C++ is accused of over-complexity.
@Rost there is always complexity in computer science. The simpler and safer we wish user code to be, the more complexity we must put in the implementation of the services it will use. Hopefully, it can be seen that the code above allows the user to create a restricted_vector using any arbitrary selection of enum values with no extra work or maintenance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.