Wanted: C ++ template idea to catch the problem, but at compile time?

We have a const array of structures, something like this:

static const SettingsSuT _table [] = {{5,1}, {1,2}, {1,1} etc.};

The structure has the following:

  • size_bytes:
  • number of objects:
  • Other metadata elements

Thus, the "total size" is size_bytes * num_items for one element. All this information is contained in a const array, available at compile time. But please note that the total size of _table is not related to the size of the EEPROM itself. _table does not reflect the EEPROM, it only describes the layout, usage, and other metadata data that we need. But you can use this metadata to determine the amount of EEPROM used.

The array simply describes the data that is stored in the external EEPROM, which has a fixed / maximum size. As you add and remove functions, the entries in the const array change. We are currently checking the total amount of data at runtime to ensure that it does not exceed the size of the EEPROM.

However, we have changed many of these runtime checks to static_assert style template checks, so the build stops immediately. I am not a template expert, so I could help with that.

So the question is: how to create a template to add the size of all elements (multiplying the values ​​of each element and then adding all the results), and then do static_assert and stop the assembly if they exceed the size of the magic EEPROM number. I considered a typical recursive factorial example of a template as one approach, but it cannot access the array, it requires a value of const (I think).

Thanks so much for any help,

+4
source share
4 answers

Your problem is that they are constant, but when calculated, they are not constant expressions:

// f is constant, but its value not known at compile-time int const f = rand() % 4; 

You need true constant expressions. You can use boost::mpl to create an mpl vector for mpl pairs, each of which has a pair of integral constants:

 using namespace boost::mpl; typedef vector< pair< int_<5>, int_<1> >, pair< int_<1>, int_<2> >, pair< int_<1>, int_<1> >, > numbers; 

Now you can iterate over elements using boost::mpl algorithms. Each int_ provides a static constant int value value you told it to. This will be evaluated with a constant expression:

 // get at the first element of the pair, located in the first element // of the vector. Then get its ::value member. int array[at<numbers, 0>::type::first::value]; 

And that will actually make this array contain 5 elements.

Boost website :: mpl Reference Guide: Here

+6
source

If you change the values ​​as template parameters, and not constructor arguments or some other initialized runtime value, then these are constants that can be used for your static_asserts.

I'm not sure how this could work for an array of structures. You may need to declare your structures using preprocessor magic to keep track of your distributions for you.

 BEGIN_EEPROM_STRUCT_TABLE() STRUCT(size, num_bytes) // etc. END_EEPROM_STRUCT_TABLE() 

This could declare your table and a constant that will add all sizes if they are constant at compile time (and that you, of course, write macros).

+1
source

This is essentially the same answer as litb, but also shows how to add dimensions without hard coding the size of the array. That is why it seems so complicated.

It is not clear which part you need help with. The following describes how you use types to track all metadata, not an array in memory. This allows you to compile time checks with enumerations that you cannot do with const int in structs. If you have more metadata, add additional parameters to the template and save them as enumeration values.

Using the Loki library: http://loki-lib.sourceforge.net/index.php?n=Main.Development . It is limited to 18 "settings" due to the implementation of MakeTypeList.

You can use TypeAt<TList, index>::Result::size_bytes (for example) to access metadata.

  #include "loki-0.1.7\include\loki\typelist.h" #include "loki-0.1.7\include\loki\static_check.h" using namespace Loki; using namespace Loki::TL; // based on the Length<> template from loki for adding up the sizes template <class TList> struct TotalSize; template <> struct TotalSize<NullType> { enum { value = 0 }; }; template <class T, class U> struct TotalSize< Typelist<T, U> > { enum { value = T::size_bytes*T::num_items + TotalSize<U>::value }; }; // struct for holding the sizes (and other meta data // if you add extra template args and enum values) template <size_t s, size_t n> struct Settings { enum { size_bytes = s, num_items = n }; }; // the table of setting structs (limited to 18) typedef MakeTypelist< Settings<5,1>, Settings<1,2>, Settings<1,1> > SettingsSuT; int _tmain(int argc, _TCHAR* argv[]) { LOKI_STATIC_CHECK(TotalSize< SettingsSuT::Result >::value == 8,is8); // this will trigger at compile time if uncommented //LOKI_STATIC_CHECK(TotalSize< SettingsSuT::Result >::value != 8,isnt8); int x = TotalSize< SettingsSuT::Result >::value; return 0; } 
0
source

From my chair, I would be inclined to try to express the configuration data as a structure so that the size can be checked at compile time (any static statement can do this). It is difficult to say whether this would be a suitable approach based on the information provided, but equally on the same grounds, I see no immediate reason to reject it.

The main translation of the table will be something like this:

 struct SettingsTable { char a[5][1];//maybe call it "featureX_conf", or whatever... char b[1][2]; char c[1][1]; }; 

(Insert appropriate compiler magic to remove indentation and alignment, although in practice a char seems generally immune to similar things in most platform / compiler combinations.)

Then, to get the dimensions, you can use sizeof if necessary. If the actual value table is still needed, it can be generated in this way. That would be a little ugly, but easy enough hidden behind a macro.

Whether this will be β€œbetter” than the more boilerplate approach probably depends on the target market.

0
source

All Articles