This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to tell the compiler a C++ const object is ROM-able ?

I defined a global static const object of the user-defined class type, assuming that the compiler may understand that object can be plce on ROM.I found that the arm compiler is really good at empty base class optimization, however, tt is not able to find out the "static const" c++ object is ROM-able...

The RAM is really a kind of scarce resource for MCUs. Is there any way to tell the compiler that a c++ const object is ROM-albe ?

By the way, the discussion about ROM-able can be found from the book Effective C++ in an Embedded Environment.

  • In ARM a "const static" object will be placed in ROM. How do you come to the idea that it would be otherwise?

  • for example

    struct
    Test{
        const int t;
        const int s;
        Test():t(5),s(3){}
    };
    
    static const Test t1;
    static const Test t2;
    
    

    the RW-data of the build output increases by 8 bytes for evere static const Test object.

  • I think initialisation by a constructor function might be a bit difficult for the compiler. You should better define it this way:

    struct Test{
      int t;
      int s;
    };
    
    static const Test t1= { 5, 3};
    static const Test t2= { 5, 3};
    static const Test t3= { 5, 3};
    static const Test t4= { 5, 3};
    static const Test t5= { 5, 3};
    static const Test t6= { 5, 3};
    

    In Opt-Level 1 you should not recognize any increase in data (neither RO, RW or ZI) - these variables will in fact be handled similar to defines / const numbers.

  • static const Test t1= { 5, 3};
    


    This may work, though, my actual code is more complicated than this. I use template metaprogramming to build compile time datastructure.I try to initialize an array at different places, therefore the code like " static const Test t1= { 5, 3}; " will not satisfy my need.

    the usage of my code is like:

    
    ARRAY_BEGIN(int, arr);
    ARRAY_ELEMENT(arr,1);
    ARRAY_ELEMENT(arr,2);
    ARRAY_ELEMENT(arr,3);
    
    ARRAY_END(arr);
    int main(){
        using namespace std;
        int len = sizeof(arr)/sizeof(int);
        cout<<"sizeof(arr)/sizeof(int)="<<len<<endl;
        const int* p = (const int*)&arr;
        for(int i=0; i<len; i++)
            cout<<p[i]<<endl;
    }
    


    the output will be:
    1 2
    3

    I've tested my code on armcc and gcc compilers respectively, it does work as I expected. The key thing is I expected that the arm compiler would recognize the objects are ROM-able. (When programming for PC with gcc compilers, we don't need to care too much about RAM)

    The implementation code is really complicated, though, I'm pretty sure the generated objects are ROM-able, at least theorecticly. Because the constructor is just like that example I posted.

    and here's part of the code in case you may doubt about the feasibility of my idea of initializing an array at different places.

    //for the  STATIC_ASSERT MACRO, you may refer to the boost library.
    #define DISTRIBUTED_ARRAY_MAX_LINES     400
    #define finline inline
    #define DISTRIBUTED_ERROR               -1
    
    #ifndef __COUNTER__
    #define __COUNTER__   __LINE__
    #endif
    
    #define ARRAY_BEGIN(ele_type,name)                              \ 
        enum { distributed_arr_##name##_begin_line                  \ 
        = __COUNTER__};                                             \ 
        template<int,typename T_##name=void>                        \ 
            struct distributed_arr##name{typedef ele_type type;}
    
    
    #define ARRAY_ELEMENT(name, value)                              \ 
            template<typename T_##name> struct                      \ 
                distributed_arr##name<__COUNTER__                   \ 
                        ,T_##name>                                  \ 
            { typedef distributed_arr##name<0>::type type;          \ 
                const type e; finline distributed_arr##name():e(value){}}
    
    
    #define ARRAY_END(name)                                     \ 
        enum { distributed_arr_##name##_end_line =              \ 
            __COUNTER__};                                       \ 
        template<int begin, int end>                            \ 
        struct array_end_##name:                                \ 
              public array_end_##name<begin,(begin+end)/2>      \ 
            , public  array_end_##name<(begin+end)/2+1,end>     \ 
        { finline array_end_##name(){}};                        \ 
        template<int begin>                                     \ 
            struct array_end_##name<begin, begin>:              \ 
              public distributed_arr##name<begin>               \ 
            { finline array_end_##name(){} };                   \ 
            template<int t>                                     \ 
            struct array_end_##name<t,DISTRIBUTED_ERROR>        \ 
            {STATIC_ASSERT(t==0);};                             \ 
        static const array_end_##name                           \ 
                <distributed_arr_##name##_begin_line,           \ 
                (distributed_arr_##name##_end_line              \ 
                    -distributed_arr_##name##_begin_line        \ 
                    >= DISTRIBUTED_ARRAY_MAX_LINES)?            \ 
                    0 : distributed_arr_##name##_end_line       \ 
                        >                                       \ 
                    name
    
    class empty_base_class_optimization_assert{
    
        ARRAY_BEGIN(int, array_1);
        ARRAY_BEGIN(char, array_2);
        ARRAY_ELEMENT(array_1,1);
        ARRAY_ELEMENT(array_2,2);
        ARRAY_ELEMENT(array_1,1);
        ARRAY_ELEMENT(array_2,2);
        ARRAY_END(array_1);
        ARRAY_END(array_2);
    
        STATIC_ASSERT(sizeof(array_1)==2*sizeof(int)
                   && sizeof(array_2)==2*sizeof(char));
    };
    

  • Sorry, that's too much for me (I would say "Poor compiler" :)).

    I think if you want to get smart code generated, you should take care that such variable initialisation is done in a straight-forward clear way. I do not understand the reason why you use constructor functions instead of the much easier direct static assignment.

  • I would use the direct static assignment too, if I could find any alternative solution in this case. Maybe I have to accept the fact that the compiler is not smart enough :) I was just wondering whether there was any compiler specific instruction to tell the compiler that an object is ROM-able.