Getting the address of a class member fuction - c++

How do I get the memory address of a class member function, I'm using a library statically that was previously setup dynamically, as the platform I ported the game to does not support dynamic libraries. It works well except for one issue.
When restoring a saved game NPCs become static as they don't continue to run the functions that were active when the game was saved. It did this by looking up the function address to get the name in the global offset table and symbol table when saving, when restoring it got the address using the name from the GOT and ST.
As this will not work with static linking I'm trying to figure out how to get the address of the exported functions to store it in a std::map with the address and a unique name when the application first starts.
The following doesn't seems to work for me for some reason. Any help would be much appreciated :).
Storing the address for the save game file in the base class using the DEFINE_FIELD macro:
// Global Savedata for Delay
TYPEDESCRIPTION CBaseEntity::m_SaveData[] =
{
DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION )
};
Derived class export from when it was using dynamic linking:
extern "C" _declspec( dllexport ) void CMultiManager( entvars_t *pev );
Derived class where ManagerThink() was exported previously when it used dynamic linking:
class CMultiManager : public CBaseEntity
{
public:
CMultiManager();
void /*EXPORT*/ ManagerThink ( void );
}
My new class constructor in the derived class to try and get the address of the member function (so I can store it in std:map with a name). Is it possible to this in the global scope rather than a constructor?
CMultiManager::CMultiManager()
{
//Try the ManagerThink function directly
void (CBaseEntity ::*ptrTemp)(void);
ptrTemp = static_cast <void (CBaseEntity::*)(void)> (ManagerThink);
void* iMemoryAddressFunc = &ptrTemp;
ALERT( at_error, "__TEST__ FUCNTION __ EXPORT ADDRESS: CMultiManager::ManagerThink (%08lx)\n", (unsigned long)iMemoryAddressFunc );
//-------------------------------------------------------------------------------
//Try the inherited m_pfnThink variable ManagerThink() is stored in as well.
void* iMemoryAddressVar = &m_pfnThink;
ALERT( at_error, "__TEST__ M_PFNTHINK __ EXPORT ADDRESS:
CMultiManager::ManagerThink (%08lx)\n", (unsigned long)iMemoryAddressVar );
}
Function check method called before restoring the game, it's using the function address stored in the save game data in the base class (function above).
void FunctionCheck( void *pFunction, char *name )
{
if (pFunction && !NAME_FOR_FUNCTION((unsigned long)(pFunction)) )
ALERT( at_error, "No EXPORT: %s:%s (%08lx)\n", STRING(pev->classname), name, (unsigned long)pFunction );
}
Debug log when running the game:
Obviously it's not going to find the export as I haven't setup anything yet as I need the address to store first, but why do the addresses not all match up? ManagerThink() has been assigned to m_pfnThink as well, so why are the first two in the log not at the same address at least?
From CMultiManager():
__TEST__ FUCNTION __ EXPORT ADDRESS: CMultiManager::ManagerThink (d008759c)
__TEST__ M_PFNTHINK __ EXPORT ADDRESS: CMultiManager::ManagerThink (01eb0e08)
From FunctionCheck();
No EXPORT: multi_manager:ManagerThink (00226c0a)
The ManagerThink() Function:
// Designers were using this to fire targets that may or may not exist --
// so I changed it to use the standard target fire code, made it a little simpler.
void CMultiManager :: ManagerThink ( void )
{
float time;
time = gpGlobals->time - m_startTime;
while ( m_index < m_cTargets && m_flTargetDelay[ m_index ] <= time )
{
FireTargets( STRING( m_iTargetName[ m_index ] ), m_hActivator, this, USE_TOGGLE, 0 );
m_index++;
}
if ( m_index >= m_cTargets )// have we fired all targets?
{
SetThink( NULL );
if ( IsClone() )
{
UTIL_Remove( this );
return;
}
SetUse ( ManagerUse );// allow manager re-use
}
else
pev->nextthink = m_startTime + m_flTargetDelay[ m_index ];
}
I'm also confused how it knew when setup dynamically which EXPORT ManagerThink ( void ); function to call for each instance of the object created, because it would have just stored the one EXPORT address in the GOT/symbol table right?
Any help/suggestions/advise would be great. Thank you :)
EDIT:
Thanks for the replies. I've managed to find a work around the issue.
I looked at a newer version of game where they updated the code to not use the GOT and ST as they needed the game on platforms that don't support dlls and were faced with the same issue. They solved it using the following macros to declare a structure and store defined pointers to member functions in the structure.
//-----------------------------------------------------------------------------
//
// Macros used to implement datadescs
//
#define DECLARE_SIMPLE_DATADESC() \
static datamap_t m_DataMap; \
static datamap_t *GetBaseMap(); \
template <typename T> friend void DataMapAccess(T *, datamap_t **p); \
template <typename T> friend datamap_t *DataMapInit(T *);
#define BEGIN_SIMPLE_DATADESC( className ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetBaseMap() { return NULL; } \
BEGIN_DATADESC_GUTS( className )
#define BEGIN_SIMPLE_DATADESC_( className, BaseClass ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetBaseMap() { datamap_t *pResult; DataMapAccess((BaseClass *)NULL, &pResult); return pResult; } \
BEGIN_DATADESC_GUTS( className )
#define DECLARE_DATADESC() \
DECLARE_SIMPLE_DATADESC() \
virtual datamap_t *GetDataDescMap( void );
#define BEGIN_DATADESC_NO_BASE( className ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetDataDescMap( void ) { return &m_DataMap; } \
datamap_t *className::GetBaseMap() { return NULL; } \
BEGIN_DATADESC_GUTS( className )
#define BEGIN_DATADESC( className ) \
datamap_t className::m_DataMap = { 0, 0, #className, NULL }; \
datamap_t *className::GetDataDescMap( void ) { return &m_DataMap; } \
datamap_t *className::GetBaseMap() { datamap_t *pResult; DataMapAccess((CBaseEntity *)NULL, &pResult); return pResult; } \
BEGIN_DATADESC_GUTS( className )
#define BEGIN_DATADESC_GUTS( className ) \
template <typename T> datamap_t *DataMapInit(T *); \
template <> datamap_t *DataMapInit<className>( className * ); \
namespace className##_DataDescInit \
{ \
datamap_t *g_DataMapHolder = DataMapInit( (className *)NULL ); /* This can/will be used for some clean up duties later */ \
} \
\
template <> datamap_t *DataMapInit<className>( className * ) \
{ \
typedef className classNameTypedef; \
static CDatadescGeneratedNameHolder nameHolder(#className); \
className::m_DataMap.baseMap = className::GetBaseMap(); \
static typedescription_t dataDesc[] = \
{ \
{ FIELD_VOID, 0, 0, 0, 0 }, /* so you can define "empty" tables */
#define END_DATADESC() \
}; \
\
if ( sizeof( dataDesc ) > sizeof( dataDesc[0] ) ) \
{ \
classNameTypedef::m_DataMap.dataNumFields = SIZE_OF_ARRAY( dataDesc ) - 1; \
classNameTypedef::m_DataMap.dataDesc = &dataDesc[1]; \
} \
else \
{ \
classNameTypedef::m_DataMap.dataNumFields = 1; \
classNameTypedef::m_DataMap.dataDesc = dataDesc; \
} \
return &classNameTypedef::m_DataMap; \
}
// replaces EXPORT table for portability and non-DLL based systems (xbox)
#define DEFINE_FUNCTION_RAW( function, func_type ) { FIELD_VOID, nameHolder.GenerateName(#function), /*{ NULL, NULL },*/ 1, FTYPEDESC_FUNCTIONTABLE, /*NULL, NULL,*/ (inputfunc_t)((func_type)(&classNameTypedef::function))},
//------------------------------------------------------------------------------

Related

BOOST_DATA_TEST_CASE with fixture support

I'm looking for fixture support in BOOST_DATA_TEST_CASE. I wrote following C++ macros for it, but may be somebody has something better?
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#define BOOST_FIXTURE_DATA_TEST_CASE_IMPL( arity, test_name, F, dataset, params ) \
struct test_name : public F { \
template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)> \
static void test_method( BOOST_DATA_TEST_CASE_PARAMS( params ) ) \
{ \
BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture entry."); \
test_name t; \
BOOST_TEST_CHECKPOINT('"' << #test_name << "\" entry."); \
BOOST_TEST_CONTEXT( "" \
BOOST_PP_SEQ_FOR_EACH(BOOST_DATA_TEST_CONTEXT, _, params)) \
t._impl(BOOST_PP_SEQ_ENUM(params)); \
BOOST_TEST_CHECKPOINT('"' << #test_name << "\" exit."); \
} \
private: \
template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)> \
void _impl(BOOST_DATA_TEST_CASE_PARAMS( params )); \
}; \
\
BOOST_AUTO_TU_REGISTRAR( test_name )( \
boost::unit_test::data::ds_detail::make_test_case_gen<test_name>( \
BOOST_STRINGIZE( test_name ), \
__FILE__, __LINE__, \
boost::unit_test::data::make(dataset) ), \
boost::unit_test::decorator::collector::instance() ); \
\
template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)> \
void test_name::_impl( BOOST_DATA_TEST_CASE_PARAMS( params ) ) \
/**/
#define BOOST_AUTO_DATA_TEST_CASE_WITH_PARAMS( test_name, dataset, ... ) \
BOOST_FIXTURE_DATA_TEST_CASE_IMPL( BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), \
test_name, BOOST_AUTO_TEST_CASE_FIXTURE, dataset, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) ) \
/**/
#define BOOST_AUTO_DATA_TEST_CASE_NO_PARAMS( test_name, dataset ) \
BOOST_AUTO_DATA_TEST_CASE_WITH_PARAMS( test_name, dataset, sample ) \
/**/
#if BOOST_PP_VARIADICS_MSVC
#define BOOST_AUTO_DATA_TEST_CASE( ... ) \
BOOST_PP_CAT( \
BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),2), \
BOOST_AUTO_DATA_TEST_CASE_NO_PARAMS, \
BOOST_AUTO_DATA_TEST_CASE_WITH_PARAMS) (__VA_ARGS__), ) \
/**/
#else
#define BOOST_AUTO_DATA_TEST_CASE( ... ) \
BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),2), \
BOOST_AUTO_DATA_TEST_CASE_NO_PARAMS, \
BOOST_AUTO_DATA_TEST_CASE_WITH_PARAMS) (__VA_ARGS__) \
/**/
#endif
Example of BOOST_DATA_TEST_CASE usage:
#include <boost/range/iterator_range.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
struct fixure
{
fixure() :
_Mydir(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("test-%%%%-%%%%-%%%%-%%%%"))
{
create_directory(_Mydir);
}
~fixure()
{
_TRY_BEGIN
remove_all(_Mydir);
_CATCH_ALL
_CATCH_END
}
boost::filesystem::path const _Mydir;
};
BOOST_FIXTURE_TEST_SUITE(suite, fixure)
namespace {
std::array<char const *, 4> const _Ourdata = { "A", "B", "C", "D" };
}
BOOST_AUTO_DATA_TEST_CASE(test, _Ourdata, _Str)
{
BOOST_REQUIRE(_Str);
BOOST_REQUIRE(is_directory(_Mydir));
auto const _File = _Mydir / _Str;
BOOST_TEST_MESSAGE(_File);
std::fstream _Stream;
_Stream.open(_File.c_str(), std::ios::out);
_Stream << _Str;
_Stream.close();
BOOST_REQUIRE(is_regular_file(_File));
}
BOOST_AUTO_TEST_SUITE_END()
Output in test_suite mode:
Running 4 test cases...
Entering test module "example"
Entering test suite "suite"
Entering test case "test"
"C:\Users\XXX\AppData\Local\Temp\test-4445-a983-8ba6-09ef\A"
Failure occurred in a following context:
_Str = A;
Leaving test case "test"; testing time: 17ms
Entering test case "test"
"C:\Users\XXX\AppData\Local\Temp\test-d4c4-c5f6-6154-7200\B"
Failure occurred in a following context:
_Str = B;
Leaving test case "test"; testing time: 10ms
Entering test case "test"
"C:\Users\XXX\AppData\Local\Temp\test-9f96-4f2c-b132-c541\C"
Failure occurred in a following context:
_Str = C;
Leaving test case "test"; testing time: 9ms
Entering test case "test"
"C:\Users\XXX\AppData\Local\Temp\test-f0cf-962c-aed3-1cf8\D"
Failure occurred in a following context:
_Str = D;
Leaving test case "test"; testing time: 10ms
Leaving test suite "suite"; testing time: 61ms
Leaving test module "example"; testing time: 76ms
*** No errors detected
Update: Test suite was updated to std::array<> using
As of Boost 1.62, you can use BOOST_DATA_TEST_CASE_F for this:
Declares and registers a data-driven test case, using a particular dataset and a fixture.
BOOST_DATA_TEST_CASE_F(my_fixture, test_case_name, dataset, var1, var2..., varN)
Reference:
https://www.boost.org/doc/libs/1_62_0/libs/test/doc/html/boost_test/utf_reference/test_org_reference/test_org_boost_test_dataset_fixture.html

Integration of a singly linked tail queue support in C++

I'm currently trying to integrate an ASN.1 decoder code written in C to the C++ Builder XE6 environment. I've encountered some problems while using the Singly linked tail queue support provided by the library, with the following call :
asn1p_wsyntx_chunk_t *wc;
while((wc = TQ_REMOVE(&(wx->chunks), next)))
asn1p_wsyntx_chunk_free(wc);
The error that I get is :
member reference type 'int' is not a pointer
The definitions of asn1p_wsyntx_chunk_t (wc) and asn1p_wsyntx_t (wx) are :
typedef struct asn1p_wsyntx_chunk_s {
enum {
WC_LITERAL,
WC_WHITESPACE,
WC_FIELD,
WC_OPTIONALGROUP
} type;
/*
* WC_LITERAL -> {token}
* WC_WHITESPACE -> {token}
* WC_FIELD -> {token}
* WC_OPTIONALGROUP -> {syntax}
*/
union {
char *token;
struct asn1p_wsyntx_s *syntax;
} content;
TQ_ENTRY(struct asn1p_wsyntx_chunk_s) next;
} asn1p_wsyntx_chunk_t;
typedef struct asn1p_wsyntx_s {
TQ_HEAD(struct asn1p_wsyntx_chunk_s) chunks;
} asn1p_wsyntx_t;
The code of the Singly linked tail is this one :
#define TQ_HEAD(type) \
struct { \
type *tq_head; \
type**tq_tail; \
}
#define TQ_MOVE(to, from) do { \
if(&(TQ_FIRST(from)) == (from)->tq_tail) { \
TQ_INIT(to); \
} else { \
(to)->tq_head = (from)->tq_head; \
(to)->tq_tail = (from)->tq_tail; \
} \
TQ_INIT(from); \
} while(0)
#define TQ_ENTRY(type) \
struct { \
type *tq_next; \
}
#define TQ_FIRST(headp) ((headp)->tq_head)
#define TQ_NEXT(el, field) ((el)->field.tq_next)
#define TQ_INIT(head) do { \
TQ_FIRST((head)) = 0; \
(head)->tq_tail = &TQ_FIRST((head)); \
} while(0)
#define TQ_FOR(var, head, field) \
for((var) = TQ_FIRST((head)); \
(var); (var) = TQ_NEXT((var), field))
/* MSVC does not have decltype(), cannot prevent side effects! */
#define TQ_ADD(head, xel, field) do { \
decltype(xel) __el = (xel); \
assert(TQ_NEXT((__el), field) == 0); \
*(head)->tq_tail = (__el); \
(head)->tq_tail = &TQ_NEXT((__el), field); \
} while(0)
#define TQ_CONCAT(head1, head2, field) do { \
if(TQ_FIRST(head2)) { \
*(head1)->tq_tail = (head2)->tq_head; \
(head1)->tq_tail = (head2)->tq_tail; \
TQ_INIT(head2); \
} \
} while(0)
/*
* Remove the first element and return it.
*/
#define TQ_REMOVE(head, field) ({ \
auto __fel = TQ_FIRST((head)); \
if(__fel == 0 \
|| (TQ_FIRST((head)) = TQ_NEXT(__fel, field)) \
== 0) { \
(head)->tq_tail = &TQ_FIRST((head)); \
} else { \
TQ_NEXT(__fel, field) = 0; \
} \
__fel; })
I've tried different casts but without success.
Thank you for your help !
EDIT :
Here is the code that I get for this call once preprocessed :
asn1p_wsyntx_chunk_t *wc;
while((wc = ({ auto __fel = (((&(wx->chunks)))->tq_head); if(__fel == 0 ||
((((&(wx->chunks)))->tq_head) = ((__fel)->next.tq_next)) == 0)
{ (&(wx->chunks))->tq_tail = &(((&(wx->chunks)))->tq_head); }
else { ((__fel)->next.tq_next) = 0; } __fel; })))
asn1p_wsyntx_chunk_free(wc);

Why native wrapped functions in Dart are such heavyweight in comparison with "DEFINE NATIVE ENTRY" functions that are very lightweight?

I cannot understand: "Why this reassurance?".
This is wrapper for custom native function from dart/runtime/vm/native_entry.cc:
It intended for the Dart programmers that want write native extensions.
void NativeEntry::NativeCallWrapper(Dart_NativeArguments args,
Dart_NativeFunction func) {
CHECK_STACK_ALIGNMENT;
VERIFY_ON_TRANSITION;
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
Isolate* isolate = arguments->isolate();
ApiState* state = isolate->api_state();
ASSERT(state != NULL);
ApiLocalScope* current_top_scope = state->top_scope();
ApiLocalScope* scope = state->reusable_scope();
TRACE_NATIVE_CALL("0x%" Px "", reinterpret_cast<uintptr_t>(func));
if (scope == NULL) {
scope = new ApiLocalScope(current_top_scope,
isolate->top_exit_frame_info());
ASSERT(scope != NULL);
} else {
scope->Reinit(isolate,
current_top_scope,
isolate->top_exit_frame_info());
state->set_reusable_scope(NULL);
}
state->set_top_scope(scope); // New scope is now the top scope.
func(args);
ASSERT(current_top_scope == scope->previous());
state->set_top_scope(current_top_scope); // Reset top scope to previous.
if (state->reusable_scope() == NULL) {
scope->Reset(isolate); // Reset the old scope which we just exited.
state->set_reusable_scope(scope);
} else {
ASSERT(state->reusable_scope() != scope);
delete scope;
}
DEOPTIMIZE_ALOT;
VERIFY_ON_TRANSITION;
}
This wrapper with all unnecessary checks that it performs at every invocation of wrapped native function makes these functions uncompetitive in comparison to what uses developers for themselves.
This is MACRO for defining native function from dart/runtime/vm/native_entry.h:
#define DEFINE_NATIVE_ENTRY(name, argument_count) \
static RawObject* DN_Helper##name(Isolate* isolate, \
NativeArguments* arguments); \
void NATIVE_ENTRY_FUNCTION(name)(Dart_NativeArguments args) { \
CHECK_STACK_ALIGNMENT; \
VERIFY_ON_TRANSITION; \
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); \
ASSERT(arguments->NativeArgCount() == argument_count); \
TRACE_NATIVE_CALL("%s", ""#name); \
{ \
StackZone zone(arguments->isolate()); \
SET_NATIVE_RETVAL(arguments, \
DN_Helper##name(arguments->isolate(), arguments)); \
DEOPTIMIZE_ALOT; \
} \
VERIFY_ON_TRANSITION; \
} \
static RawObject* DN_Helper##name(Isolate* isolate, \
NativeArguments* arguments)
I know that it works directly with RawObject. This is normal.
But I can not find in it all of these tests, which are performed in each call, as in the above wrapper.
I lose heart when I see that my functions work on the 3000% slower than the analogues defined via DEFINE_NATIVE_ENTRY.
P.S
My native function that does NOTHING and does not returns ANYTHING works on the 500% slower than (for example) this function.
#define TYPED_DATA_GETTER(getter, object, access_size) \
DEFINE_NATIVE_ENTRY(TypedData_##getter, 2) { \
GET_NON_NULL_NATIVE_ARGUMENT(Instance, instance, arguments->NativeArgAt(0)); \
GET_NON_NULL_NATIVE_ARGUMENT(Smi, offsetInBytes, arguments->NativeArgAt(1)); \
if (instance.IsTypedData()) { \
const TypedData& array = TypedData::Cast(instance); \
RangeCheck(offsetInBytes.Value(), access_size, \
array.LengthInBytes(), access_size); \
return object::New(array.getter(offsetInBytes.Value())); \
} \
if (instance.IsExternalTypedData()) { \
const ExternalTypedData& array = ExternalTypedData::Cast(instance); \
RangeCheck(offsetInBytes.Value(), access_size, \
array.LengthInBytes(), access_size); \
return object::New(array.getter(offsetInBytes.Value())); \
} \
const String& error = String::Handle(String::NewFormatted( \
"Expected a TypedData object but found %s", instance.ToCString())); \
Exceptions::ThrowArgumentError(error); \
return object::null(); \
} \
Is there any way to write lightweight native functions that not requires all of these scope?
This is an old question, but native libraries are definitely not the greatest and are pretty heavy-weight. These days we typically recommend that users look at using dart:ffi for C-interop, which is more performant than native extensions and arguably much easier to use.

Why is DECLARE_DYNAMIC & IMPLEMENT_DYNAMIC nessary for DYNAMIC_DOWNCAST?

I have two classes:
/*Switch.h*/
class CSwitch : public CDeviceEntity {}
/*EndSystem.h*/
class CEndSystem : public CDeviceEntity {}
but when I use:
CDeviceEntity* dev = NULL;
dev = topo->headList[i]->node;
if ( DYNAMIC_DOWNCAST( CEndSystem, dev ) != NULL ) {}
"DYNAMIC_DOWNCAST" always returns not NULL while dev is kind of class CEndSystem or class CSwitch.
If use:
/*Switch.h*/
class CSwitch : public CDeviceEntity { DECLARE_DYNAMIC(CSwitch) }
and
/*Switch.cpp*/
IMPLEMENT_DYNAMIC(CSwitch, CDeviceEntity)
/*EndSystem.h*/
class CEndSystem : public CDeviceEntity { DECLARE_DYNAMIC(CEndSystem) }
and
/*EndSystem.cpp*/
IMPLEMENT_DYNAMIC(CEndSystem, CDeviceEntity)
"DYNAMIC_DOWNCAST" returns NULL or not NULL according to class CEndSystem or class CSwitch.
Why "DECLARE_DYNAMIC" and "IMPLEMENT_DYNAMIC" are nessary for "DYNAMIC_DOWNCAST"?
/*Algorithm.h*/
static int getESNum();
/*Algorithm.cpp*/
int CAlgorithm::getESNum()
{
int count = 0;
CDeviceEntity* dev = NULL;
for (int i = 0; i < topo->nodeNum; i++)
{
dev = topo->headList[i]->node;
if ( DYNAMIC_DOWNCAST( CEndSystem, dev ) != NULL )
{
count++;
}
}
return count;
}
/*Algorithm.h*/
static int getSWNum();
/*Algorithm.cpp*/
int CAlgorithm::getSWNum()
{
int count = 0;
CDeviceEntity* dev = NULL;
for (int i = 0; i < topo->nodeNum; i++)
{
dev = topo->headList[i]->node;
if ( DYNAMIC_DOWNCAST(CSwitch, dev) != NULL )
{
count++;
}
}
return count;
}
And the functions are called in serialization when saving the document.
DYNAMIC_DOWNCAST is a throw back to how you used to have to do dynamic casting before RTTI information was available from the compiler. The casting information is created using the macros DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC which use the class CRuntimeClass to decide if the cast is valid.
DYNAMIC_DOWNCAST simply does this:
CObject* AFX_CDECL AfxDynamicDownCast(CRuntimeClass* pClass, CObject* pObject)
{
if (pObject != NULL && pObject->IsKindOf(pClass))
return pObject;
else
return NULL;
}
The DECLARE_DYNAMIC macro adds this code:
#define DECLARE_DYNAMIC(class_name) \
protected: \
static CRuntimeClass* PASCAL _GetBaseClass(); \
public: \
static const CRuntimeClass class##class_name; \
static CRuntimeClass* PASCAL GetThisClass(); \
virtual CRuntimeClass* GetRuntimeClass() const; \
Add IMPLEMENT_DYNAMIC adds this code:
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \
CRuntimeClass* PASCAL class_name::_GetBaseClass() \
{ return RUNTIME_CLASS(base_class_name); } \
AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
&class_name::_GetBaseClass, NULL, class_init }; \
CRuntimeClass* PASCAL class_name::GetThisClass() \
{ return _RUNTIME_CLASS(class_name); } \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return _RUNTIME_CLASS(class_name); }
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)
I imagine few people still use this for new projects, instead preferring the C++ standard dynamic_cast<> call (along with static_cast and reinterpret_cast).

macro expansion in C/C++

I have this macro from someone's code:
#define Q_DEF_PROTOTYPE( Type, Name ) Type (*Name)
#define COPY_FP( pDest, pSrc ) (*((void**)(&(pDest)))) = ((void*)(pSrc))
#define LIB_QUERY(lib_handle, proc) dlsym(lib_handle, proc)
#define Q_DEF_PROTOTYPE( Type, Name ) \
COPY_FP( p->Name, LIB_QUERY( g_library, STRINGIZE(FUNC(Name)) ) ); \
void dummy_##Name
Not sure, what "void dummy_##Name" does? Thanks.
It replace the ##Name with the value of Name parameter as string.
Q_DEF_PROTOTYPE(myType, objectName) => void dummy_objectName