views:

383

answers:

4

Hello,

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

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

the structure has the following:

  • size_bytes:
  • num_items:
  • Other "meta data" members

So the "total size" is size_bytes*num_items for a single element. All of this information is in the const array, available at compile time. But, please note, the total size of _table is not related to the size of the EEPROM itself. _table does not mirror the EEPROM, it only describes the layout, usage, and other "meta data" type information we need. But, you can use this meta data to determine the amount of EEPROM we are using.

The array simply describes the data that is stored in an external EEPROM, which has a fixed/maximum size. As features are added and removed, the entries in the const array changes. We currently have a runtime check of the total size of the data to insure that it does not exceed the EEPROM size.

However, we have been changing over many of these runtime checks to static_assert style template checks, so that the build stops immediately. I'm not a template expert, so could use some help on this one.

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

thank you very much for any help,

+1  A: 

If you change the values to be template parameters rather than constructor arguments or some other runtime initialized value, then they are constants that can be used for your static_asserts.

I'm not sure how this could work for an array of the structs though. You may need to declare your structs using some macro preprocessor magic so that it keeps track of your allocations for you.

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

This could possibly declare your table and a const that adds up all the sizes, provided they are constant at compile time (and that you write the macros appropriately, of course).

Jeff Yates
+5  A: 

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

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

What you need are true constant expressions. You can use boost::mpl to make up a mpl vector of mpl pairs, each with 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 the items of it using boost::mpl algorithms. Each int_ is exposes a static int constant value set to the value you told it. That will evaluate to 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 would actually make that array contain 5 elements.

Website of boost::mpl Reference Manual: Here

Johannes Schaub - litb
A: 

This is essentially the same answer as litb, but also shows how to add the sizes up w/o hard coding the array size. That's why it appears so complicated.

It's not clear what part you need help with. Below is how you use types to track all the meta data rather than an array in memory. This allows compile time checks with enums that you can't do with const int in structs. If you have more metadata, add additional parameters in the template for Settings and store them as enum values.

Using the Loki library: http://loki-lib.sourceforge.net/index.php?n=Main.Development. It's limited to 18 "Settings" because of how MakeTypeList is implemented.

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

   #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;
   }
Tony Lee
A: 

From my armchair I would be inclined to try to express the configuration data as a struct so that the size can be checked at compile time (any static assert can do this). Hard to say whether this would be a suitable approach, based on the information provided, but equally, on the same grounds I can't see any immediate reason to dismiss it.

A basic translation of the table would be something like:

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

(Insert suitable compiler magic to remove padding and alignment, though in practice char seems generally immune to this kind of thing on the majority of platform/compiler combinations.)

Then to get sizes one can use sizeof as appropriate. If the actual table of values is still necessary, it could be generated this way. It would be a bit ugly, but easily-enough hidden behind a macro.

Whether this would be "better" than a more template-heavy approach probably depends on the target market.

brone
Yes, we used the sizeof approach on other tables. But, I guess I wasn't 100% clear. This table is more like a "meta data" table. It describes the EEPROM, its structure, and how each element is to be used. It is not an exact "sizeof" replica of the EEPROM.thanks
M. Esh.