0

What is the best way to design a function where the allowed enum value changes based on the HW selected for example. The function might look like this, this won't compile but hopefully you get the idea.

enum class EHWUnit
{
Unit0,
Unit1,
Unit2
};

enum class EHWType
{
Type1,
Type2,
Type3,
Type4
};

SelectHWinput( const EHWUnit aUnit, const EHWType aHWType, const unit8_t aSelect )
{

if( aHWType & 1 == 0)

ArrayIS( 2,4,6,8,10)

else

 ArrayIS( 1,3,5,7,9)

 element = ArrayIS(aSelect)

 WriteToRegister( Address, bits, element);
}

Does anyone know of a way to make it better to select the element based on type? I would prefer to somehow link those even elements to the type at compile time and not select them at run time, using one function is there anyway to make this code more fool proof?

user1876942
  • 1,411
  • 2
  • 20
  • 32
  • I'm not sure what you're trying to achieve, but can't you use TMP to make that compile-time decision? – JorenHeit Jun 30 '14 at 11:11
  • Are you looking for something like `const int bit = !(aHWType & 1); ArrayIs(1 + bit, 3 + bit, 5 + bit, 7 + bit, 9 + bit);`? Or more sophisticated solution that involves templates (@JorenHeit might mean it by saying "TMP")? – xaizek Jun 30 '14 at 11:14
  • If the type is even then the elements are even, so if Type1 is chosen ( I made a typo ) it should be Type0, that is even and if aSelect is 3 then the value of 8 is passed to the HW register. – user1876942 Jun 30 '14 at 11:21
  • Maybe I can use templates, I will investigate further. – user1876942 Jun 30 '14 at 11:21

1 Answers1

0

A shot in the dark, as I'm not sure what exactly it is you want to do. It seems like you want to make some compile-time decisions, which could look something like this:

enum EHWUnit
{
    Unit0,
    Unit1,
    Unit2
};

enum EHWType
{
    Type1,
    Type2,
    Type3,
    Type4
};


template <int Select>
struct ArrayIS
{
    static constexpr int const array[] = {1, 3, 5, 7, 9};
};
template <int Select>
constexpr int const ArrayIS<Select>::array[];

template <>
struct ArrayIS<0>
{
    static constexpr int const array[] = {2, 4, 6, 8, 10};
};    
constexpr int const ArrayIS<0>::array[];

template <EHWUnit aUnit, EHWType aHWType, int aSelect>
struct SelectHWinput
{
    static constexpr int get()
    {
        return ArrayIS<static_cast<int>(aHWType) & 1>::array[aSelect];
    }
};

int main()
{
    // To illustrate that the value is selected at compile-time, a
    // static assertion that will always fail:
    static_assert(SelectHWinput<Unit0, Type1, 0>::get() < 0, "Works, value selected at compile-time");
}

In the code above, Template Metaprogramming (TMP) is used to select a value of one of the arrays at compile-time. The correct array is chosen by supplying the Select template non-type parameter. Because these arrays are declared constexpr, a constexpr member-function can be used to extract one of its element at compile-time. The static_assert proves that it works.

If you want to construct the arrays more flexibly at compile-time, I suggest looking at this question of mine, where I describe TMP-functors to construct static arrays at compile-time: g++ (4.7.2) bug or feature, when initializing static arrays at compile-time?

EDIT: You said you were interested in the Array implementation:

template <typename T, size_t Count, template <typename, T> class Function, T Current, T ... Elements>
struct Array_: public Array_<T, Count - 1, Function, Function<T, Current>::result, Elements ..., Current>
{};


template <typename T, template <typename, T> class Function, T Current, T ... Elements>
struct Array_<T, 0, Function, Current, Elements...>
{
    constexpr static std::array<T, sizeof...(Elements)> array = {{Elements ...}};
};

template <typename T, size_t Count, T Start = T(), template <typename, T> class Function = Null>
struct Array: public Array_<T, Count, Function, Start>
{};

Here, the last one is the one you'll use, using a Template Metaprogramming functor that tells the compiler how to generate the next element from the previous one. This is a limitation, but it's the best I could come up with at the time.

Three simple functors that you could use:

template <typename T, T Value>
struct Null
{
    enum { result = Value };
};

template <typename T, T Value>
struct Increment
{
    enum { result = Value + 1 };
};

template <typename T, T Value>
struct Decrement
{
    enum { result = Value - 1 };
};

For example, if you need an array with incremental values from 1 to 100, you would do this:

std::array<int, 100> arr = Array<int, 100, 1, Increment>::array;

This array is then allocated and populated at compile-time.

Community
  • 1
  • 1
JorenHeit
  • 3,877
  • 2
  • 22
  • 28
  • Thanks, I will need to look through this and see if I can use it. It can be I have many arrays, e.g. 16. It might get messy and I would like to pass an enum as aSelect, I will need to study and maybe get back to you. – user1876942 Jun 30 '14 at 12:48
  • @user1876942 If you have that many arrays, I can provide you with my `Array` implementation (which is missing from the question I linked to). Let me know if you're interested. – JorenHeit Jun 30 '14 at 14:57
  • Ideally I would like to have aSelect as an enum too. Any ideas about that? – user1876942 Jul 01 '14 at 05:56
  • @user1876942 added the Array implementation. I don't get your question about the aSelect enum. You can just add an enum and change the templates a little right? – JorenHeit Jul 15 '14 at 10:45