Expansion of Nested Macros with reinterpret cast in c++ - c++

I stumbled across the below code and really found it complex to understand the nested macro and type casting in it.
Also when i tried to compile the code , i have encountered an error
Would need an explanantion of the below code.
why BEGIN_STATE_MAP and END_STATE_MAP set as labels in Motor.h , This is really new to me
Thanks in advance
Motor.h
// the Motor state machine class
class Motor : public StateMachine
{
public:
Motor() : StateMachine(ST_MAX_STATES) {}
// external events taken by this state machine
void Halt();
void SetSpeed(MotorData*);
private:
// state machine state functions
void ST_Idle();
void ST_Stop();
void ST_Start(MotorData*);
void ST_ChangeSpeed(MotorData*);
// state map to define state function order
BEGIN_STATE_MAP
STATE_MAP_ENTRY(ST_Idle)
STATE_MAP_ENTRY(ST_Stop)
STATE_MAP_ENTRY(ST_Start)
STATE_MAP_ENTRY(ST_ChangeSpeed)
END_STATE_MAP
// state enumeration order must match the order of state
// method entries in the state map
enum E_States {
ST_IDLE = 0,
ST_STOP,
ST_START,
ST_CHANGE_SPEED,
ST_MAX_STATES
};
};
#endif //MOTOR_H
what are BEGIN_STATE_MAP and END_STATE_MAP, This definition i found i really new,
BEGIN_STATE_MAP and END_STATE_MAP are the Macros defined in the below header file.
StateMachine.h
#ifndef STATE_MACHINE_H
#define STATE_MACHINE_H
#include <stdio.h>
#include "EventData.h"
struct StateStruct;
// base class for state machines
class StateMachine
{
public:
StateMachine(int maxStates);
virtual ~StateMachine() {}
protected:
enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN };
unsigned char currentState;
void ExternalEvent(unsigned char, EventData* = NULL);
void InternalEvent(unsigned char, EventData* = NULL);
virtual const StateStruct* GetStateMap() = 0;
private:
const int _maxStates;
bool _eventGenerated;
EventData* _pEventData;
void StateEngine(void);
};
typedef void (StateMachine::*StateFunc)(EventData *);
struct StateStruct
{
StateFunc pStateFunc;
};
#define BEGIN_STATE_MAP \
public:\
const StateStruct* GetStateMap() {\
static const StateStruct StateMap[] = {
#define STATE_MAP_ENTRY(entry)\
{ reinterpret_cast<StateFunc>(entry) },
#define END_STATE_MAP \
{ reinterpret_cast<StateFunc>(NULL) }\
}; \
return &StateMap[0]; }
#define BEGIN_TRANSITION_MAP \
static const unsigned char TRANSITIONS[] = {\
#define TRANSITION_MAP_ENTRY(entry)\
entry,
#define END_TRANSITION_MAP(data) \
0 };\
ExternalEvent(TRANSITIONS[currentState], data);
#endif
EventData.h
#ifndef EVENT_DATA_H
#define EVENT_DATA_H
class EventData
{
public:
virtual ~EventData() {};
};
#endif //EVENT_DATA_H
While i tried to compile the code above.Below is the error that was encountered
Error
-------------- Build: Debug in StateMachine (compiler: GNU GCC Compiler)---------------
mingw32-g++.exe -Wall -fexceptions -g -pedantic -Wzero-as-null-pointer-constant -std=c++0x -Wextra -Wall -c C:\Users\xprk569\StateMachine\main.cpp -o obj\Debug\main.o
In file included from C:\Users\xprk569\StateMachine\main.cpp:2:0:
C:\Users\xprk569\StateMachine\Motor.h: In member function 'virtual const StateStruct* Motor::GetStateMap()':
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
{ reinterpret_cast<StateFunc>(entry) },
^
C:\Users\xprk569\StateMachine\Motor.h:29:9: note: in expansion of macro 'STATE_MAP_ENTRY'
STATE_MAP_ENTRY(ST_Idle)
^
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
{ reinterpret_cast<StateFunc>(entry) },
^
C:\Users\xprk569\StateMachine\Motor.h:30:9: note: in expansion of macro 'STATE_MAP_ENTRY'
STATE_MAP_ENTRY(ST_Stop)
^
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
{ reinterpret_cast<StateFunc>(entry) },
^
C:\Users\xprk569\StateMachine\Motor.h:31:9: note: in expansion of macro 'STATE_MAP_ENTRY'
STATE_MAP_ENTRY(ST_Start)
^
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the '&' ?)
{ reinterpret_cast<StateFunc>(entry) },
^
C:\Users\xprk569\StateMachine\Motor.h:32:9: note: in expansion of macro 'STATE_MAP_ENTRY'
STATE_MAP_ENTRY(ST_ChangeSpeed)
^
C:\Users\xprk569\StateMachine\StateMachine.h:43:39: error: invalid cast from type 'int' to type 'StateFunc {aka void (StateMachine::*)(EventData*)}'
{ reinterpret_cast<StateFunc>(NULL) }\
^
C:\Users\xprk569\StateMachine\Motor.h:33:5: note: in expansion of macro 'END_STATE_MAP'
END_STATE_MAP
^
Process terminated with status 1 (0 minute(s), 0 second(s))
5 error(s), 0 warning(s) (0 minute(s), 0 second(s))
Can some please explain why is the macro written that way in Motor.h,
why is it declared like that in StateMachine.h and
why is the error being thrown ?
Thanks in Advance

It looks like this code depends on some nonstandard compiler extensions/errors.
To get it to compile (no idea if it will actually work) you need to replace the function names with full qualified member function pointers:
e.g.
BEGIN_STATE_MAP
STATE_MAP_ENTRY(&Motor::ST_Idle)
STATE_MAP_ENTRY(&Motor::ST_Stop)
STATE_MAP_ENTRY(&Motor::ST_Start)
STATE_MAP_ENTRY(&Motor::ST_ChangeSpeed)
END_STATE_MAP
After that, you need to figure out a way to overcome the non-conforming cast:
/tmp/gcc-explorer-compiler116314-75-1uiyu0/example.cpp: In member function 'virtual const StateStruct* Motor::GetStateMap()':
44 : error: invalid cast from type 'long int' to type 'StateFunc {aka void (StateMachine::*)(EventData*)}'
{ reinterpret_cast<StateFunc>(NULL) }\
^
83 : note: in expansion of macro 'END_STATE_MAP'
This cast is completely illegal. If I were you I would throw the code in the trash and rewrite - or use a proven state machine framework like boost meta state machine or boost statechart.

So you're quickly learning why Macros are no-nos in readable C++. If you get an error, you must expand the macro out to identify where the error is, also you cannot debug into them in most IDEs.
Anyway that said lets get to the expanding, their all the same error so we'll just look at the first one:
C:\Users\xprk569\StateMachine\Motor.h:29:9: note: in expansion of macro STATE_MAP_ENTRY
STATE_MAP_ENTRY(ST_Idle)
C:\Users\xprk569\StateMachine\StateMachine.h:40:40: error: invalid use of member (did you forget the & ?)
{ reinterpret_cast<StateFunc>(entry) },
So this is complaining about line 29: STATE_MAP_ENTRY(ST_Idle) So lets expand that:
{ reinterpret_cast<StateFunc>(entry) },
Obviously this is bad syntax all together outside the scoping of BEGIN_STATE_MAP and END_STATE_MAP, so in debugging many Macros you'd also have to look at the scoping macros... sometimes they may not be clearly named or delineated unfortunately, but let's finish defining the line we got the error on first. What is this StateFunc we're trying to cast to?
typedef void (StateMachine::*StateFunc)(EventData *);
It's a pointer to a member function which returns a void and accepts an EventData *. And alarm bells should be going off. You cannot cast to that! ST_Idle is of the format: void (StateMachine::*)() so you cannot cast to void (StateMachine::*StateFunc)(EventData *). This is the same problem for all your functions passed into the macros none of them return a void and take an EventData*, so even if you fix the syntax, these reinterpret_casts will always return a pointer to a method which is invalid to call, meaning this entire block of Macros is pointless at best and toxic at worst. In the current state you may just as well use none of these Macros or if you need to define the method just do:
BEGIN_STATE_MAP
END_STATE_MAP
But if you were going to change your method declarations to something more like:
void ST_Idle(EventData*);
Then you'd need to use this syntax:
STATE_MAP_ENTRY(&Motor::ST_Idle)
If you're not down with the method pointers they are quite complex. I've typed up a quick example here: http://ideone.com/nL0HnQ Feel free to comment with questions.
EDIT:
To expand the Macros here we'll get:
public: // BEGIN_STATE_MAP
const StateStruct* GetStateMap() { // BEGIN_STATE_MAP
static const StateStruct StateMap[] = { // BEGIN_STATE_MAP
{ reinterpret_cast<StateFunc>(ST_Idle) } // STATE_MAP_ENTRY(ST_Idle)
{ reinterpret_cast<StateFunc>(ST_Stop) } // STATE_MAP_ENTRY(ST_Stop)
{ reinterpret_cast<StateFunc>(ST_Start) } // STATE_MAP_ENTRY(ST_Start)
{ reinterpret_cast<StateFunc>(ST_ChangeSpeed) } // STATE_MAP_ENTRY(ST_ChangeSpeed)
{ reinterpret_cast<StateFunc>(NULL) } // END_STATE_MAP
}; // END_STATE_MAP
return &StateMap[0]; } // END_STATE_MAP
So this set of macros will:
Set the scope to public
Declare the method GetStateMap
Declare StateMap statically local to GetStateMap, it will be an array of StateStructs
On the first call of the GetStateMap method StateMap will be initialized to contain method pointers to ST_Idle, ST_Stop, ST_Start, ST_ChangeSpeed, and NULL reinterpret_cast to StateFuncs
Define GetStateMap to return the StateMap array

Related

Compilation time message (after preprocessor) to use with `if constexpr`

I am using an if constexpr to test the presence of a method in a class. If the method is not present, I wish to tell the user the function was ignored and he should implement it but it is not mandatory.
The idea is to give a message similar to #warning but the preprocessor is processed before the template thus this will never work.
Is there any compilation time feedback alternative that came with C++17? Or any planned for C++20?
Runnable example
template <typename State>
void callStateFunction(const State& state) {
if constexpr (false) {
state.method();
} else {
#warning The method State::method() was not implemented
}
}
Not a great solution, I suppose, but...
If your compiler activate all warnings (-Wall for g++ and clang++, by example), you can substitute the #warning row with something that generate a warning.
By example, an unused (maybe with a talking name) variable.
I've tried with
template <typename State>
void callStateFunction(const State& state) {
if constexpr (false) {
state.method();
} else {
int method_not_implemented[sizeof(State)];
}
}
and calling with a non-method value (callStateFunction(1), by example), I get
prog.cc: In instantiation of 'void callStateFunction(const State&) [with State = int]':
prog.cc:13:23: required from here
prog.cc:7:9: warning: unused variable 'method_not_implemented' [-Wunused-variable]
7 | int method_not_implemented[sizeof(State)];
| ^~~~~~~~~~~~~~~~~~~~~~
from g++ (head 11.0.0) and
prog.cc:7:9: warning: unused variable 'method_not_implemented' [-Wunused-variable]
int method_not_implemented[sizeof(State)];
^
prog.cc:13:4: note: in instantiation of function template specialization 'callStateFunction<int>' requested here
callStateFunction(1);
^
from clang++ (head 11.0.0)
I suggest that the unused variable depends from the template typename (State) otherwise, if I define a non-dependent variable as
int method_not_implement;
I get a warning from clang++
prog.cc:7:9: warning: unused variable 'method_not_implemented' [-Wunused-variable]
int method_not_implemented;
^
also without calling the function with a non-method object.

Perl XS unused variable 'Perl___notused' warnings

I am learning how to call C++ code from Perl and to start I am trying to create a basic C++ object from a Perl script.
To do this, I started by executing the h2xs command:
h2xs -A -nMyClass
Then I added the following two arguments to the generated Makefile.PL to use the g++ compiler.
CC => 'g++',
LD => 'g++',
I created my simple class in the .xs file and wrote the XS code to map it with Perl
MyClass.xs
#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#ifdef __cplusplus
}
#endif
class MyClass {
public:
MyClass(int value) {
value_ = value;
}
~MyClass() {}
int value() { return value_; }
void set_value(int value) {
value_ = value;
}
private:
int value_;
};
MODULE = MyClass PACKAGE = MyClass
MyClass *
MyClass::new(int value)
void
MyClass::DESTROY()
int
MyClass::value()
void
MyClass::set_value(int value)
Then I created the typemap file to map the new type to Perl.
typemap
TYPEMAP
MyClass * O_OBJECT
######################################################################
OUTPUT
# The Perl object is blessed into 'CLASS', which should be a
# char* having the name of the package for the blessing.
O_OBJECT
sv_setref_pv( $arg, CLASS, (void*)$var );
######################################################################
INPUT
O_OBJECT
if( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) )
$var = ($type)SvIV((SV*)SvRV( $arg ));
else{
warn( \"${Package}::$func_name() -- $var is not a blessed SV reference\" );
XSRETURN_UNDEF;
}
Finally I created a simple test.
t/MyClass.t
use Test::More tests => 2;
BEGIN { use_ok('MyClass') };
my $obj = MyClass->new(1);
ok($obj->isa('MyClass'), 'MyClass object constructed');
I then successfully built the code and ran the tests.
perl Makefile.PL
make
make test
Although everything works fine, I get some warnings with the build:
MyClass.c: In function 'void XS_MyClass_new(PerlInterpreter*, CV*)':
MyClass.c:95: warning: unused variable 'Perl___notused'
MyClass.c: In function 'void XS_MyClass_DESTROY(PerlInterpreter*, CV*)':
MyClass.c:119: warning: unused variable 'Perl___notused'
MyClass.c: In function 'void XS_MyClass_value(PerlInterpreter*, CV*)':
MyClass.c:145: warning: unused variable 'Perl___notused'
MyClass.c: In function 'void XS_MyClass_set_value(PerlInterpreter*, CV*)':
MyClass.c:174: warning: unused variable 'Perl___notused'
MyClass.c: In function 'void boot_MyClass(PerlInterpreter*, CV*)':
MyClass.c:203: warning: unused variable 'Perl___notused'
I searched all over trying to find the cause for these warnings, and can't figure out what is going on. All of the warnings seem come from the same repeated section in the code that occurs at the beginning of every function definition.
inside MyClass.c
XS(XS_MyClass_new); /* prototype to pass -Wmissing-prototypes */
XS(XS_MyClass_new)
{
#ifdef dVAR
dVAR; dXSARGS; // <-- warning occurs here
#else
dXSARGS;
#endif
// function body continues...
Can someone please tell me the root cause of these warnings?
I am using Perl v5.10.1 and g++ version 4.4.7
The solution is to use a newer version of Perl. v5.10.1 is very outdated and these problems don't occur in later versions.
I am posting an answer to my own question because it was answered in the comments, but an official answer was never posted.

Unit testing a vector string

I have this really simple line of code in my production-code(A.cpp) as follows:
std::string A::getString(int i) {
return sVect_[i];
}
with the header as follows:
class A{
public:
std::string getString(int i);
...
private:
vector<std::string> sVect_;
...
};
I've been trying to test the getString() function using googletest but an error keeps popping out:
error: invalid conversion from 'char* (*)(const char*, int)throw ()' to 'int'
error: initializing argument 1 of 'std::string A::getString(i)'
This was my test program:
TEST(ATest, getString){
A a;
EXPECT_EQ("c", a.getString(i));
}
I couldn't quite grasp the workaround of the vector string and how to call it in my test program without ever changing the production code. I even use the hack, adding #define statements, to access the private member but still couldn't do it.
How do my test actually looks like to successfully call that function?
Note: I'm on Linux and using gcc. Thank you in advance guys.
Perhaps the error message is misleading. Have you defined i globally somewhere else? To me it looks like in the local scope because it does not know what the value of the variable i is, it is misbehaving in an unexpected way
TEST(ATest, getString){
A a;
EXPECT_EQ("c", a.getString(i)); //here what is the 'i' and where is it defined
}

Pointer to member, within class

I am trying create a typedef for a template that needs a pointer to member, from within a class. The best way to describe it is to show the minimal sample code:
template<typename T, int T::*MV>
struct Bar
{
const int &value(const T &t) const
{
return (t.*MV);
}
};
struct Foo
{
int baz;
typedef Bar<Foo, &Foo::baz> GetBaz; // Compiler error
};
typedef Bar<Foo, &Foo::baz> GetFooBaz; // Compiles just fine
int main(int argc, char* argv[])
{
Foo f = { 42 };
Foo::GetBaz b; // Fails to compile
GetFooBaz b2; // Fine
int val = b.value(f); // Fails to compile because of above
int val2 = b2.value(f); // Fine
}
I am not necessarily dead-set on getting access to a member pointer in this way, I would be fine just knowing the offset to the variable and having the Bar::value function perform trickery.
As a last resort, I suppose I could use a traits class since the would move the definition outside of the class, but I would prefer to be able to declare the typedef near the variable being used.
And, to answer the "why would you want to do that" question, this is all for an IoC container that closely represents the way MEF (C# works).
The specific compiler I am using is VC12, but it would be nice if VC11 supported it as well.
Thanks
EDIT:
Here are the error messages
1>------ Build started: Project: MemVarBug, Configuration: Debug Win32 ------
1> MemVarBug.cpp
1>memvarbug.cpp(20): error C2327: 'Foo::baz' : is not a type name, static, or enumerator
1>memvarbug.cpp(20): error C2065: 'baz' : undeclared identifier
1>memvarbug.cpp(20): error C2975: 'MV' : invalid template argument for 'Bar', expected compile-time constant expression
1> memvarbug.cpp(7) : see declaration of 'MV'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
you can do the following trick:
declare baz to be int[1];
array of size one, in this case calling baz will return the pointer and calling *baz will return the value.

C++ Garbage collection, templates and operator overriding - can't understand why it throws an error

I am trying to compile a file which defines a garbage collection template and several supporting classes with use of operator overloading. I've tried to run this through MSVC++ 2008, and the compile stops at this particular class:
// (The collector defines gc_object_generic_base which
// inherits from gc_object_generic_base and optionally adds
// collector-specific properties.)
template<class garbage_collector>
class gc_object_base : public garbage_collector::gc_object_collector_base {
public:
gc_object_base() {
garbage_collector::constructing_gc_object_base(this);
}
static void* operator new(size_t sz,
block_construction_locker_base* lock = block_construction_locker<garbage_collector>().get_this())
{
return garbage_collector::allocate(sz, lock);
}
static void operator delete(void* p, block_construction_locker_base* lock) {
return garbage_collector::deallocate(p);
}
static void operator delete(void* p) {
return garbage_collector::deallocate(p);
}
private:
// TODO: are arrays worth implementing?
static void* operator new[](size_t sz) {
assert(0);
return 0;
}
};
Truncated output for brevity's sake:
2>------ Build started: Project: Test, Configuration: Debug Win32 ------
2>Compiling...
2>FlashTest.cc
2>C:\test\gameswf\base\tu_gc.h(133) : error C2059: syntax error : 'string'
2> C:\test\gameswf\base\tu_gc.h(151) : see reference to class template instantiation 'tu_gc::gc_object_base' being compiled
2>C:\test\gameswf\base\tu_gc.h(135) : error C2091: function returns function
2>C:\test\gameswf\base\tu_gc.h(135) : error C2802: static member 'operator new' has no formal parameters
2>C:\test\gameswf\base\tu_gc.h(135) : error C2333: 'tu_gc::gc_object_base::operator new' : error in function declaration; skipping function body
Any ideas on where I should start looking at?
I just had the same pattern of errors in a different project. It seems to happen when someone has done this somewhere:
#define new new(someArgs, someMoreArgs, etc)
It's targeted at simple 'new' expressions, but it breaks down if anyone tries to declare any more 'operator new' thingies.
In the opening lines
template<class garbage_collector>
class gc_object_base : public garbage_collector::gc_object_collector_base {
garbage_collector appears twice here, once as the template parameter and another as an outer-class to gc_object_collector_base, but as it is a template parameter does it not require "typename" here thus:
template<class garbage_collector>
class gc_object_base : public typename garbage_collector::gc_object_collector_base {
There is no mention of string in your code, that I can see. But I would start by ensuring that you first #include <string> before your class delcaration, and then make sure you use std::string as your declarator, rather than just string.