我们有一个表格,我们想要静态初始化,但是 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):注意:遇到非常数(子)表达式
但是我在这里看不到什么是非常数。任何提示?
答案 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