静态const数组在MSVC中动态初始化?

时间:2016-03-16 10:30:02

标签: c++ arrays visual-c++ static-initialization

我们有一个表格,我们想要静态初始化,但是 MSVC(2015.1及更旧的版本)会生成动态初始值设定项。

以下是展示问题的简化代码:

#define idaapi __stdcall
#define MAXSTR 1024
typedef int error_t;
typedef unsigned char uchar;


struct psymbol_t
{
  short what;           /* -1 - is error,                       */
                        /* 0 - any symbol,don't skip it         */
                        /* else lxtype_t                        */
  short callNumber;     /* Number in table of metasymbols       */
                        /* -1 - no metasymbol                   */
                        /* Error code if what == -1             */
  uchar  nextNumber;    /* Number in current table              */
                        /* 0xFF - end                           */
  uchar  actNumber;     /* Number in Actions table              */
                        /* 0xFF - no action                     */
};

class parser_t;
typedef error_t (idaapi parser_t::*action_t)(void);
typedef error_t (idaapi parser_t::*nexttoken_t)(void);

struct token_t
{
  int type;          ///< see \ref lx_
  char str[MAXSTR];     ///< idents & strings
};

class basic_parser_t
{
  nexttoken_t gettok;
  const psymbol_t *const *Table;
  const action_t *Actions;
  bool got_token;
public:
  token_t ahead;
  //bool exported_parse(int goal) { return basic_parser_parse(this, goal); }
};


class parser_t: public basic_parser_t {
public:
/*  0 */ error_t idaapi aArrayStart(void);
/*  1 */ error_t idaapi aComplexEnd(void);
/*  2 */ error_t idaapi aObjectStart(void);
/*  3 */ error_t idaapi aObjectKvpNew(void);
/*  4 */ error_t idaapi aObjectKvpKey(void);
/*  5 */ error_t idaapi aConstant(void);
};


static const action_t Acts[] =
{
/*  0 */ &parser_t::aArrayStart,
/*  1 */ &parser_t::aComplexEnd,
/*  2 */ &parser_t::aObjectStart,
/*  3 */ &parser_t::aObjectKvpNew,
/*  4 */ &parser_t::aObjectKvpKey,
/*  5 */ &parser_t::aConstant
};

使用/FAs /c进行编译会在.asm文件中生成dynamic initializer for 'Acts'函数,而不是一个漂亮的常量数组。

const替换为constexpr会产生此警告:

  

t.cpp(54):错误C2131:表达式未评估为常数
     t.cpp(54):注意:遇到非常数(子)表达式

但是我在这里看不到什么是非常数。任何提示?

3 个答案:

答案 0 :(得分:8)

  ??__EActs@@YAXXZ PROC     ; `dynamic initializer for 'Acts'', COMDAT

我假设你是抱怨的人。单通道编译模型在这里是一个更大的障碍,编译器不能对parser_t类的继承模型做任何假设,它只有前向声明可以使用。成员函数指针看起来不同,具体取决于类是使用单继承,多继承还是虚拟继承。

您需要帮助并告诉编译器适当的非标准extension keyword。修正:

 class __single_inheritance parser_t;

表生成现在变为:

?Acts@@3QBQ8parser_t@@AGHXZB DD FLAT:?aArrayStart@parser_t@@QAGHXZ ; Acts
    DD  FLAT:?aComplexEnd@parser_t@@QAGHXZ
    DD  FLAT:?aObjectStart@parser_t@@QAGHXZ
    etc...
CONST   ENDS

再也没有动态初始化程序。

答案 1 :(得分:2)

我不确定为什么函数指针地址不能被提取为常量 - 这需要从Microsoft开发人员那里询问 - 但是我能够解决这个问题 - 如果你在基类中引入一些虚函数 - 然后它能够​​正确找出函数指针地址。

此代码无法编译:

#include <stdio.h>          // printf

class CBase
{
public:
    void func1()
    {
    }
};

class Test: public CBase
{
public:
    virtual void func2()
    {
    }

    void DoTest1( char* s )
    {
        printf("DoTest1: %s\r\n", s);
    }

    void DoTest2( char* s )
    {
        printf( "DoTest1: %s\r\n", s );
    }
};

typedef void (Test::*funcaction) ( char* s );

static constexpr funcaction g_funs[] =
{
    &Test::DoTest1,
    &Test::DoTest2,
};

此代码编译良好:

#include <stdio.h>          // printf

class CBase
{
public:
    virtual void func1()
    {
    }
};

class Test: public CBase
{
public:
    virtual void func2()
    {
    }

    void DoTest1( char* s )
    {
        printf("DoTest1: %s\r\n", s);
    }

    void DoTest2( char* s )
    {
        printf( "DoTest1: %s\r\n", s );
    }
};

typedef void (Test::*funcaction) ( char* s );

static constexpr funcaction g_funs[] =
{
    &Test::DoTest1,
    &Test::DoTest2,
};

差异是什么 - 没有任何线索。 : - )

答案 2 :(得分:1)

问题是parser_t中的函数不是static,因此编译器不知道要为Acts[]的元素分配什么内存地址。如果您创建函数static并为它们提供定义,那么编译器可以进行关联并将指针设置为正确的值。您可能还需要更改typedef的{​​{1}}(或制作副本)。

action_t

如果你希望函数保持非 - typedef error_t (idaapi *action_t)(void); class parser_t: public basic_parser_t { public: /* 0 */ static error_t idaapi aArrayStart(void) { /*...*/ } /*...*/ }; static const action_t Acts[] = { /* 0 */ &parser_t::aArrayStart, /*...*/ }; (例如static是一个抽象类,或者由于其他原因,它的函数必须是非parser_t),那么{{1}不能static定义,你需要实例化Acts[](或其完全定义的子类),然后通过该对象将static的元素分配给函数。 / p>

parser_t

INFO: Creating a Function Pointer to a C++ Member Function
Pointer declaration: Pointers to member functions