开发者

c++ fatal error C1061 with large switch, metaprogramming

开发者 https://www.devze.com 2023-01-21 00:11 出处:网络
that\'s the code: static inline void shrinkData(const vector<Data> &data, unsigned short shrinkType){

that's the code:

static inline void 
shrinkData(const vector<Data> &data, unsigned short shrinkType){
    #define CASE_N(N) \
    case(N): \
        ptr = MemoryManager开发者_开发百科::requestMemory(n*sizeof(ShrinkData<N>)); \
        for(int i=0; i<n; i++){ \
            new(ptr) ShrinkData<N>(data[i]); \
            ptr+=sizeof(ShrinkData<N>); \
        } \
        return;


    int n = data.size();
    char* ptr;

    switch(shrinkType){
    case(0):
        return;
    CASE_N(1)
    CASE_N(2)
    CASE_N(3)
    ....
    CASE_N(255)
}

now I get a fatal "error C1061: compiler limit : blocks nested too deeply" on line CASE_N(124)

can someone please tell me why this happens? actually the nesting should not be deeper than 2, no?

Thanks!

//edit: the requested constructor (the constructor works just fine without this switch function)

enum {//maximally 16 to fit in a unsigned short!
    EID_POSITION        =   1, //bit1
    EID_T               =   2, //bit2
    EID_GEOMNORMAL      =   4, //bit3
    EID_NORMAL          =   8, //bit4
    EID_TANGENTS        =  16, //bit5
    EID_TEXCOORDS       =  32, //bit6
    EID_RAYDIR          =  64, //bit7
    EID_RECURSIONDEPTH  = 128  //bit8
};



template<unsigned, unsigned>
struct IDataMember{
    IDataMember(){}
    IDataMember(const Data &iData){}
};


template<>
struct IDataMember<EID_POSITION, EID_POSITION>{
    IDataMember(): position(Vector3(0,0,0)){}
    IDataMember(const Data &iData):position(iData.position){}
    Vector3 position;
};

... the same kind of template specialisation for each type in the enums...

template<unsigned members>
struct ShrinkData
    :public IDataMember<members & EID_POSITION, EID_POSITION>
    ,public IDataMember<members & EID_T, EID_T>
    ,public IDataMember<members & EID_GEOMNORMAL, EID_GEOMNORMAL>
    ,public IDataMember<members & EID_NORMAL, EID_NORMAL>
    ,public IDataMember<members & EID_TANGENTS, EID_TANGENTS>
    ,public IDataMember<members & EID_TEXCOORDS, EID_TEXCOORDS>
    ,public IDataMember<members & EID_RAYDIR, EID_RAYDIR>
    ,public IDataMember<members & EID_RECURSIONDEPTH, EID_RECURSIONDEPTH>
{
    ShrinkData()
        :IDataMember<members & EID_POSITION, EID_POSITION>()
        ,IDataMember<members & EID_T, EID_T>()
        ,IDataMember<members & EID_GEOMNORMAL, EID_GEOMNORMAL>()
        ,IDataMember<members & EID_NORMAL, EID_NORMAL>()
        ,IDataMember<members & EID_TANGENTS, EID_TANGENTS>()
        ,IDataMember<members & EID_TEXCOORDS, EID_TEXCOORDS>()
        ,IDataMember<members & EID_RAYDIR, EID_RAYDIR>()
        ,IDataMember<members & EID_RECURSIONDEPTH, EID_RECURSIONDEPTH>(){}


    ShrinkData(const Data &iData)
        :IDataMember<members & EID_POSITION, EID_POSITION>(iData)
        ,IDataMember<members & EID_T, EID_T>(iData)
        ,IDataMember<members & EID_GEOMNORMAL, EID_GEOMNORMAL>(iData)
        ,IDataMember<members & EID_NORMAL, EID_NORMAL>(iData)
        ,IDataMember<members & EID_TANGENTS, EID_TANGENTS>(iData)
        ,IDataMember<members & EID_TEXCOORDS, EID_TEXCOORDS>(iData)
        ,IDataMember<members & EID_RAYDIR, EID_RAYDIR>(iData)
        ,IDataMember<members & EID_RECURSIONDEPTH, EID_RECURSIONDEPTH>(iData){}

};


According to this link there is a "feature" in compiler that allows only for limited number of loops. Never happened to me. Try to put ptr initialization and the following for loop in a block. Another solution is to create template function that covers the whole snippet, so that the macro becomes something like this:

#define CASE_N(N) \
case(N): \
    ptr = requestAndInitialize<N>(data); \
    return;


I suppose that error message is bogus. There likely really is a compiler limit, but it's unlikely the block nesting.

Anyway, what happens if you put that code for each case into its own function template and just call that?
Also, inlining this function is very likely not gaining you anything, since it will request memory and execute a loop. Function call overhead must be neglectable compared to that. (No matter how few iterations that loop takes, just setting it up by itself is probably half as expensive as calling a function.)
Finally, I'd try to get rid of the macro, just in case.

The code might then look like this:

// Beware, brain-compiled code ahead!
template<unsigned short N>
void do_it(int n)
{
  char* ptr = MemoryManager::requestMemory(n*sizeof(ShrinkData<N>));
  for(int i=0; i<n; i++){
    new(ptr) ShrinkData<N>(data[i]);
    ptr+=sizeof(ShrinkData<N>);
  }
}

static void 
shrinkData(const vector<Data> &data, unsigned short shrinkType)
{
  const std::vector<Data>::size_type n = data.size();
  switch(shrinkType){
    case   0: break
    case   1: do_it<  1>(n); break;
    case   2: do_it<  2>(n); break;
    .
    .
    .
    case 254: do_it<254>(n); break;
    case 255: do_it<255>(n); break;
}


Just speculating, but the function is tagged inline and it uses templates as well, maybe that's part of where the extra nesting is coming from.

However, more likely IMO, the resource limit you're hitting probably isn't in actuality so clear cut as "levels of nesting" but rather that is the perceived most common cause of hitting it so that's what the error message refers to.


I just came across this in some auto-generated code; for loops rather than a switch, but the same issue. Rather than putting all the loops in their own blocks - making the code somewhat uglier in the process - I apparently solved the issue by moving the declaration of the iterator outside the loops, C style.

I suspect the issue might reappear if we add too many more loops, as I believe it is due to running out of space to store scope data, but this is much nicer in the meantime.

Edit: sure enough the issue reappeared 1.5 years later. And adding the extra nesting level fixed it. I guess this answer was more for my own benefit than anyone else's.

0

精彩评论

暂无评论...
验证码 换一张
取 消