We have a table we'd like to initialize statically, however MSVC (2015.1, and older versions too) generates a dynamic initializer instead.
Here's the simplified code demonstrating the issue:
#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
};
compilation with /FAs /c
produces a dynamic initializer for 'Acts'
function in the .asm file instead of a nice constant array.
replacing last const
by constexpr
produces this warning:
t.cpp(54): error C2131: expression did not evaluate to a constant
t.cpp(54): note: a non-constant (sub-)expression was encountered
However I'm not seeing what is non-constant here. Any hints?
??__EActs@@YAXXZ PROC ; `dynamic initializer for 'Acts'', COMDAT
I'll assume that's the one you are complaining about. The single-pass compilation model is the larger obstacle here, the compiler cannot make any assumptions about the inheritance model for the parser_t
class, it only has the forward declaration to work with. Member function pointers look different depending on whether the class uses single, multiple or virtual inheritance.
You need to help and tell the compiler with the appropriate non-standard extension keyword. Fix:
class __single_inheritance parser_t;
And the table generation now changes to:
?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
And no dynamic initializer anymore.
I'm not sure why function pointer address cannot be extracted as a constant - this needs to be asked from Microsoft developers, however - I was able to walk around this problem - if you introduce some virtual function in base class - it then be able to figure out function pointer addresses correctly.
This code does not compile:
#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,
};
This code compiles fine:
#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,
};
What's the difference - no clue. :-)
The issue is that the functions in parser_t
are not static
, so the compiler does not know what memory address to assign to the elements of Acts[]
. If you make the functions static
and provide definitions for them, then the compiler can make the associations and set the pointers to the right values. You will probably also need to alter the typedef
for action_t
(or make a copy).
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,
/*...*/
};
If you want the functions to remain non-static
(e.g. parser_t
is an abstract class or its functions must be non-static
for other reasons), then Acts[]
cannot be static
ally defined and you'll need to instantiate parser_t
(or its fully-defined child class) and then assign the elements of Acts[]
to the functions via that object.
class parser_t: public basic_parser_t {
public:
/* 0 */ error_t idaapi aArrayStart(void) { /* defined here or in child class */ }
/*...*/
};
parser_t parser = new parser_t();
const action_t Acts[] =
{
/* 0 */ &parser::aArrayStart,
/*...*/
};
INFO: Creating a Function Pointer to a C++ Member Function
Pointer declaration: Pointers to member functions
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With