I'm trying to write a production test software for an embedded system where I could write a single test script (which ideally is just C++ code), where parts are executed on the host computer and part on the DUT (Device Under Test). Communication is through a serial port.
One important goal here is to reduce code size on the embedded side, without reducing readability of the test output. So my level 0 objective, you could say a warmup exercise, is to be able to write something like this:
//TestScript.cpp
START_TESTS()
...
const unsigned pot1res = testPotentiometer(pot1);
TEST_PRINT("Potentiometer 1 test result %u", pot1res);
const unsigned pot2res = testPotentiometer(pot2);
TEST_PRINT("Potentiometer 2 test result %u", pot2res);
...
END_TESTS()
which would compile through preprocessor trickery and selective compilation on the embedded side to
const unsigned pot1res = testPotentiometer(pot1);
write_uart(123); //Unique id, perhaps from __COUNTER__
write_uart(pot1res);
const unsigned pot2res = testPotentiometer(pot2);
write_uart(124); //Unique id, perhaps from __COUNTER__
write_uart(pot2res);
and on the host
std::array gTestStrings = {
... other strings ....
TestString{123, "Potentiometer 1 test result %u", unsigned_tag},
TestString{124, "Potentiometer 2 test result %u", unsigned_tag},
... more strings ....
};
The purpose of the latter is then of course that the host software simply listens to the UART for the unique id's, then looks up the needed parameters from gTestStrings, receives them, and prints out the message to its test log. Note that the strings have disappeared entirely from the embedded side.
The embedded side here is of course easy, just define the TEST_PRINT macro in the obvious way, and supporting varargs etc shouldn't be too difficult. However, it's not clear how define the host side, since the code in between the macros has to disappear entirely. I'm pretty sure I can deal with getting the unsigned_tags etc correctly with some templates etc.
Standard C++17 is appreciated, but GCC/Clang specifics are allowed if needed, the preprocessor will obviously play heavily into this etc. The syntax of the macros can of course also be adjusted where necessary.
On the basis of "use templates to do the same thing with different types" you could simply define a templated class or method which invoke different implementations according to whether in the embedded context or host context.
Pseudocode example:
#include <sstream>
#include <string>
class Mock
{
protected:
// TODO : output values array / map - by - testID
public:
unsigned readPot(int const testID)
{
// TODO : return value from the values array/map
}
};
class ActualDevice
{
public:
unsigned readPot(int const testID)
{
// TODO : write/read the device
}
};
//DEVICE_IMPL must implement a method with this signature:
//unsigned readPot(int const testID)
template<typename DEVICE_IMPL>
void runTests()
{
DEVICE_IMPL device;
std::string testOutput;
//TODO : substitute with for-loop to run all tests
int testID = 1;
{
unsigned output = device.readPot(testID);
std::stringstream accum;
accum << "Test " << testID << " output = " << (int)output << std::endl;
testOutput = accum.str();
// TODO : do something with testOutput
}
}
void testHost ()
{
runTests<Mock>();
}
void testDevice()
{
runTests<ActualDevice>();
}
#ifdef __linux__ // or whatever you have there
#define START_TESTS() std::array gTestStrings = {
#define END_TESTS() };
#define TEST(expr, str) TestString{__LINE__, str, unsigned_tag},
#else
#define START_TESTS() {
#define END_TESTS() }
#define TEST(expr, ...) do{ \
const auto _val = (expr); \
write_uart(__LINE__); \
write_uart(_val); \
} while(0)
#endif
int main() {
START_TESTS()
TEST(testPotentiometer(pot1), "Potentiometer 1 test result %u");
TEST(testPotentiometer(pot2), "Potentiometer 2 test result %u");
END_TESTS()
}
I do not particularly like that design - seems not flexible enough for me. But such code could serve as a template for you to write something better. I added the expression inside the TEST macro - that way it can be removed on the host side, and used on the device side.
Another option could be X-macros:
pot_tests.h
X(123, pot1, "Potentiometer 1 test result %u")
X(124, pot2, "Potentiometer 2 test result %u")
embedded.c
START_TESTS()
...
#define X(id, pot, text) \
do { \
const unsigned potres = testPotentiometer(pot); \
write_uart(id); \
write_uart(potres); \
TEST_PRINT(text, potres); \
} while(0);
#include "pot_tests.h"
#undef X
...
END_TESTS()
host.cpp
std::array gTestStrings = {
... other strings ....
#define X(id, pot, text) TestString{id, text, unsigned_tag},
#include "pot_tests.h"
#undef X
... more strings ....
};
Related
I am trying to print IT SUCCESS\nET SUCCESS\n using following code but it's failing in compilation with error error: ‘printds’ was not declared in this scope which I know is because it's taking macro input as ds literal. Does anyone know how to do this? The use case is that there are several printXX() functions which should be called based on value passed in macro.
#include <stdio.h>
#define FOO(val) { \
print ## val(); \
}
void printIT() { printf("IT SUCCESS\n"); }
void printET() { printf("ET SUCCESS\n"); }
int main() {
const char* ds = "IT", es = "ET";
FOO(ds); FOO(es);
return 0;
}
You can change
FOO(ds); FOO(es);
to
FOO(IT); FOO(ET);
Because macro substitutions happen before your code is compiled.
But you can define a function called FOO like
#include <stdio.h>
#include <iostream>
using namespace std;
void printIT() { printf("IT SUCCESS\n"); }
void printET() { printf("ET SUCCESS\n"); }
void FOO(const string str)
{
if(str=="IT")
printIT();
else
printET();
}
int main()
{
const char* ds = "IT",*es="ET";
FOO(ds);FOO(es);
return 0;
}
it's taking macro input as ds literal.
Yes, that's to be expected. Preprocessor macros are expanded at compile time. The arguments to function-like macros are the literal source-code tokens that appear between the parentheses in the macro invocation. These have no additional meaning to the preprocessor.
Does anyone know how to do this? The use case is that there are several printXX() functions which should be called based on value passed in macro.
Again, macros are expanded, to source code (approximately), at compile time. The process does not and cannot take into account C++ runtime semantics such as converting variables' identifiers into corresponding values.
If runtime dynamic function dispatch based on variables' values is what you're after then you need an altogether different mechanism. You could use ordinary conditional statements to select between different function calls, for example. If you wanted to be even more dynamic then you could consider preparing a lookup table of function pointers, and using that to select and call appropriate functions.
In comments, you added
I've several methods queryServers, queryNodes, queryTargets which I want to call using above trick.
You may be able to accomplish something similar to what you ask via templates or overloaded functions. These mechanisms, too, operate at compile time, so they have no access to runtime information such as variables' values, but they do know about and rely upon C++ data types.
Alternatively, perhaps you're looking for the Strategy pattern.
The first thing you need to know is that the Macros are preprocessor directives which are a fragment of code with a given name. if you use macro name in your program it will replace that code fragment into that place you use the macro name at compile time first stage called Pre-processing stage.
#include <stdio.h>
#define FOO(val) { \
print ## val(); \
}
void printIT() { printf("IT SUCCESS\n"); }
void printET() { printf("ET SUCCESS\n"); }
int main() {
const char* ds = "IT", es = "ET";
FOO(ds); FOO(es);
return 0;
}
In your code, you try to input ds and es variables into FOO function like macro. But ds and es variables declared in program stack only when you run the program. At the compile-time, it just treats them as only texts. Therefore macro function input it takes as text ds and es and replaced with val. That's why you got the compile time error. The following code fragment I have changed is working as you expected.
#include <stdio.h>
#define FOO(val) { \
print ## val(); \
}
void printIT() { printf("IT SUCCESS\n"); }
void printET() { printf("ET SUCCESS\n"); }
int main() {
const char* ds = "IT", *es = "ET";
FOO(IT); FOO(ET);
return 0;
}
If you are interested you can find more about Macros in the following resources.
GCC online documentation and Article about Macros . Also, you can view the preprocessed code using g++ -E (your cpp file name). Thanks.
Possible solution without MACRO:
void printIT() { printf("IT SUCCESS\n"); }
void printET() { printf("ET SUCCESS\n"); }
void foo(std::string_view s)
{
static const std::map<std::string_view, void(*)()> funcs{
{"IT", &printIT},
{"ET", &printET}
};
auto it = funcs.find(s);
if (it != funcs.end()) {
(*it->second)();
}
}
int main() {
const char* ds = "IT";
const char* es = "ET";
foo(ds); foo(es);
}
Demo
Right now, IMO, Google typed-parameterized tests are annoying. You have to do:
template <typename fixtureType>
class testFixtureOld : public ::testing::Test
{
};
// Tell google test that we want to test this fixture
TYPED_TEST_CASE_P(testFixtureOld);
// Create the tests using this fixture
TYPED_TEST_P(testFixtureOld, OIS1Old)
{
TypeParam n = 0;
EXPECT_EQ(n, 0);
}
TYPED_TEST_P(testFixtureOld, OIS2Old)
{
TypeParam n = 0;
EXPECT_EQ(n, 0);
}
// Register the tests we just made
REGISTER_TYPED_TEST_CASE_P(testFixtureOld, OIS1Old, OIS2Old);
// Run the tests
typedef ::testing::Types<char, int, unsigned int> TypesTestingOld;
INSTANTIATE_TYPED_TEST_CASE_P(RunOldTests, testFixtureOld, TypesTestingOld);
Much of this stuff seems it could be automated. For example:
#define TYPED_TESTS_P(fixture, testName1, test1, testName2, test2) TYPED_TEST_CASE_P(fixture); TYPED_TEST_P(fixture, testName1) test1 TYPED_TEST_P(fixture, testName2) test2 REGISTER_TYPED_TEST_CASE_P(fixture, testName1, testName2);
#define RUN_TYPED_TESTS_P(testSuiteName, fixture, type1, type2, type3) typedef::testing::Types<type1, type2, type3> TypesTesting; INSTANTIATE_TYPED_TEST_CASE_P(testSuiteName, fixture, TypesTesting);
template <typename fixtureType>
class testFixtureNew : public ::testing::Test
{
};
// Make our tests. This tells google test that we want to test this fixture,
// creates the tests using this fixture, and registers them.
TYPED_TESTS_P(testFixtureNew,
OISNew,
{
TypeParam n = 0;
EXPECT_EQ(n, 0);
},
OIS2New,
{
TypeParam n = 0;
EXPECT_EQ(n, 0);
}
)
// Run the tests
RUN_TYPED_TESTS_P(RunNewTests, testFixtureNew, char, int, unsigned int);
(where those macros could be easily expanded to a very large size and then they will be sufficient for most use)
This works, however, this syntax is rather abnormal, so I'd like to make it more normal looking so it is more readable. This requires a way of doing something like this:
#include <std>
using namespace std;
#define PassIntoThenListOut(inArg, fun1, fun2) something
PassIntoThenListOut(6,
int a(int foo)
{
cout << "5+first = " << (5+foo);
},
int b(int bar)
{
cout << "10+second = " << (10+bar);
}
)
// Should output:
// 5+first = 11
// 10+second = 16
// ArgumentNames: foo bar
which I'm not sure is possible to do. Is this possible?
I would simply post the last bit of code but others seem to think that it is too obscure to imagine a use case so I wanted to provide that as well.
I have run into your problem using gtest and celero.
I use a combination of macros and a python script to automate much of the boiler plate code.
here are the macros i use
#define DEFINE(name,threads) \
void name(); \
REGISTER(name,threads) \
#define REGISTER(name,threads) \
SINGLE(name) \
THREADED(name,threads) \
#define THREADED(name, num_of_threads) \
void name##Threaded(){ \
std::vector< std::thread > threads; \
for(int i=0; i<num_of_threads; i++){ \
threads.push_back( std::thread([this](){this->name##Single();})); \
}; \
for(auto &t : threads){ \
t.join(); \
}; \
};
#define SINGLE(name) \
void name##Single(){ \
this->name(); \
};
and i use it like this
`template<typename T>
class LocationTest : public ::testing::Test{
protected:
location<T> policy;
DEFINE(mallocfreetest, LOCATION_THREADS)
DEFINE(copytest, LOCATION_THREADS)
};'
template<typename T>
void LocationTest<T>::mallocfreetest(){
void* p=NULL;
p=policy.New(10);
policy.Delete(p);
EXPECT_TRUE(p);
};
template<typename T>
void LocationTest<T>::copytest(){
int a=1;
int* a_ptr=&a;
int b=0;
int* b_ptr=&b;
policy.MemCopy(a_ptr,b_ptr,sizeof(int));
EXPECT_EQ(1,b);
};
template<>
void LocationTest<device>::copytest(){
size_t size=sizeof(int);
int a=1;
int* a_d=static_cast<int*>( policy.New(size) );
ASSERT_TRUE(a_d);
int b=0;
int* b_d=static_cast<int*>( policy.New(size) );
ASSERT_TRUE(b_d);
cudaMemcpy(a_d,&a,size,cudaMemcpyHostToDevice);
cudaMemcpy(b_d,&b,size,cudaMemcpyHostToDevice);
policy.MemCopy(a_d,b_d,size);
cudaMemcpy(&b,b_d,size,cudaMemcpyDeviceToHost);
EXPECT_EQ(1,b);
};
#define HOST host
#define UNIFIED unified
#define DEVICE device
#define PINNED pinned
//python:key:policy=HOST UNIFIED DEVICE PINNED
//python:key:tests=copytestSingle mallocfreetestSingle copytestThreaded mallocfreetestThreaded
//python:template=TEST_F($LocationTest<|policy|>$,|tests|){this->|tests|();}
//python:start
//python:include=location.test
#include"location.test"
//python:end
#undef HOST
#undef UNIFIED
#undef DEVICE
#undef PINNED
#undef LOCATION_THREADS
at the end you can see where the python script comes in. it parses the information in the comments and generates all the TEST_F(****) code by iteration though all possible cominations of keys and putting it into the include file. the python script also overcomes the issues with macros and templates in c++, namely, if you have
TEST_F(example,test_name){test_code();};
the preprocessor will think there are three paramaters in TEST_F
so you need to write it like this
typedef example class_type_1_type_2;
TEST_F(class_type_1_type_2, test_name){test_code();};
(in to tell the python script to pull something into a type def just put the type between two $ like in the example above)
you call the python script like
coverage.py -i test_file.cpp
import os
import re
import copy
import getopt
import sys
#find functions,types,locations list;
def getoutputfile():
global contents
functionRegex=re.compile(r"//python:include=(.*)")
fun=functionRegex.findall(contents)
return fun[0]
def getkey():
global contents
params={}
functionRegex=re.compile(r"//python:key:(\w*)=(.*)")
fun=functionRegex.findall(contents)
for i in range( len(fun) ):
params[ fun[i][0] ]=fun[i][1].split(" ")
return params
def get_template():
global contents
functionRegex=re.compile(r"//python:template=(.*)")
fun=functionRegex.findall(contents)
return fun
def getnumlines(array,temp):
num=1
for i in array:
num*=i
return num*len(temp)
def initializeMaxArray(array):
global keys
global paramaters
for i in range(keys):
j=paramaters.keys()[i]
array[i]=len( paramaters[j])
def increment(a,k):
if k<keys:
a[k]+=1
if a[k]>=max_array[k]:
a[k]=0
a=increment(a,k+1)
# *******************read in file and data
a,b=getopt.getopt(sys.argv[1:],"i:")
input_file=a[0][1]
source_file=open(input_file,"r")
contents=source_file.read()
source_file.close()
#*****************initalize varaibles
paramaters=getkey()
template=get_template()
keys=len( paramaters.keys() )
max_array=[0]*keys
initializeMaxArray(max_array)
lines=getnumlines(max_array,template)
contents_new=[]
for i in range(len(template)):
contents_new+=[template[i]]*(lines/len(template))
for i in range(len(contents_new)):
contents_new[i]+='\n'
temps=len(template)
array=[[0]*keys]*(lines*temps)
for i in range(lines-1):
array[i+1]=copy.copy(array[i])
increment(array[i+1],0)
#variable replacement
for j in range(lines):
for i in range(keys):
key=paramaters.keys()[i]
x=array[j][i]
result=contents_new[j].replace("|"+key+"|",paramaters[key][x])
contents_new[j]=result
#typedef insertion
typedef_list=[];
typedefreg=re.compile(r".*\$(.+)\$.*")
for k in range(len( contents_new) ):
matches=typedefreg.findall(contents_new[k] )
for j in matches:
match=j
clear={"<":"_",">":"_",",":"_"," ":""}
for i in clear.keys():
match= match.replace(i,clear[i] )
for j in matches:
typedef=r"typedef "+j+" "+match+"; \n" rep="$"+j+"$"
contents_new[k]=contents_new[k].replace(rep,match)
typedef_list.append(typedef)
contents_new.insert(0,"//Tests/benchmarks \n")
typedef_list.insert(0,"//typedefs \n")
output=typedef_list+contents_new
outputfile=getoutputfile()
#write out to file
destination_file=open(outputfile,'w')
destination_file.write( "".join(output) )
destination_file.close()
im sorry if the long post is annoying but i spent allot of time trying to speed up writing unit test and benchmarks and i hope these things can help you as well,
WARNING: the python script is probably note the best written in the world but works for what i need it for.
if you have anyquest about how to use this come feel free to ask me more or if you need it to do something it doesnt do i could add it to it. im working on putting up on github.
Recently I have been reading Effective C++ Second Edition by Scott Meyers to improve on C++ best practices. One of his listed items encourages C++ programmers to avoid pre-processor macros and 'prefer the compiler'. He went as far as saying there are almost no reasons for macro in C++ aside from #include and #ifdef/#ifndef.
I agree with his reasoning, as you can accomplish the following macro
#define min(a,b) ((a) < (b) ? (a) : (b))
with the following C++ language features
template<class T>
inline const T & min(const T & a, const T & b) {
return a < b ? a : b;
}
where inline gives the compiler the option to remove the function call and insert inline code and template which can handle multiple data types who have an overloaded or built in > operator.
EDIT-- This template declaration will not completely match the stated macro if the data type of a and b differ. See Pete's comment for an example.
However, I am curious to know if using macros for debug logging is a valid use in C++. If the method I present below is not good practice, would someone be kind to suggest an alternative way?
I have been coding in Objective-C for the last year and one of my favorite 2D engines (cocos2d) utilized a macro to create logging statements. The macro is as follows:
/*
* if COCOS2D_DEBUG is not defined, or if it is 0 then
* all CCLOGXXX macros will be disabled
*
* if COCOS2D_DEBUG==1 then:
* CCLOG() will be enabled
* CCLOGERROR() will be enabled
* CCLOGINFO() will be disabled
*
* if COCOS2D_DEBUG==2 or higher then:
* CCLOG() will be enabled
* CCLOGERROR() will be enabled
* CCLOGINFO() will be enabled
*/
#define __CCLOGWITHFUNCTION(s, ...) \
NSLog(#"%s : %#",__FUNCTION__,[NSString stringWithFormat:(s), ##__VA_ARGS__])
#define __CCLOG(s, ...) \
NSLog(#"%#",[NSString stringWithFormat:(s), ##__VA_ARGS__])
#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
#define CCLOG(...) do {} while (0)
#define CCLOGWARN(...) do {} while (0)
#define CCLOGINFO(...) do {} while (0)
#elif COCOS2D_DEBUG == 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) do {} while (0)
#elif COCOS2D_DEBUG > 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) __CCLOG(__VA_ARGS__)
#endif // COCOS2D_DEBUG
This macro provides for incredible utility which I will want to incorporate in my C++ programs. Writing a useful log statement is as simple as
CCLOG(#"Error in x due to y");
What is even better, is that if the COCOS2D_DEBUG is set to 0, then these statements never see the light of day. There is no overhead for checking a conditional statement to see if logging statements should be used. This is convenient when making the transition from development to production. How could one recreate this same effect in C++?
So does this type of macro belong in a C++ program? Is there a better, more C++ way of doing this?
First, Scott's statement was made at a time when macros were
considerably overused, for historical reasons. While it is
generally true, there are a few cases where macros make sense.
Of of these is logging, because only a macro can automatically
insert __FILE__ and __LINE__. Also, only a macro can
resolve to absolutely nothing (although based on my experience,
this isn't a big deal).
Macros such as you show aren't very frequent in C++. There are
two usual variants for logging:
#define LOG( message ) ... << message ...
which allows messages in the form " x = " << x, and can be
completely suppressed by redefining the macro, and
#define LOG() logFile( __FILE__, __LINE__ )
where logFile returns a wrapper for an std::ostream, which
defines operator<<, and permits such things as:
LOG() << "x = " << x;
Done this way, all of the expressions to the right of LOG()
will always be evaluated, but done correctly, no formatting will
be done unless the log is active.
There are "right" things to use macros for and there are bad uses of macros. Using macros where functions work is a bad idea. Using macros where functions DON'T do the same thing is perfectly good in my book.
I quite often use constructs like this:
#defien my_assert(x) do { if (!x) assert_failed(x, #x, __FILE__, __LINE__); } while(0)
template<typename T>
void assert_failed(T x, const char *x_str, const char *file, int line)
{
std::cerr << "Assertion failed: " << x_str << "(" << x << ") at " << file << ":" << line << std::endl;
std::terminate();
}
Another trick using the stringizing "operator" is something like this:
enum E
{
a,
b,
c,
d
};
struct enum_string
{
E v;
const char *str;
};
#define TO_STR(x) { x, #x }
enum_string enum_to_str[] =
{
TO_STR(a),
TO_STR(b),
TO_STR(c),
TO_STR(d),
};
Saves quite a bit of repeating stuff...
So, yes, it's useful in some cases.
I need to generate a series of sequential numbers throughout my code at compile time. I tried "__COUNTER__" in a way like this:
void test1()
{
printf("test1(): Counter = %d\n", __COUNTER__);
}
void test2()
{
printf("test2(): Counter = %d\n", __COUNTER__);
}
int main()
{
test1();
test2();
}
And the result was perfect as I expected:
test1(): Counter = 0
test2(): Counter = 1
Then I spread "__COUNTER__" out in different .cpp files:
In Foo.cpp:
Foo::Foo()
{
printf("Foo::Foo() with counter = %d\n", __COUNTER__);
}
In Bar.cpp:
Bar::Bar()
{
printf("Bar::Bar() with counter = %d\n", __COUNTER__);
}
In Main.cpp:
int main()
{
Foo foo;
Bar bar;
}
The result was:
Foo::Foo() with counter = 0
Bar::Bar() with counter = 0
It looks to me that "__COUNTER__" is provided as a per compile unit variable.
What I'd like to have is a global counter that's effective throughout the code.
This is used for testing in a debug build where I want to achieve this goal:
Imagine that I have try/catch blocks throughout the code (a subsystem or a module within multiple .cpp files). At run time the program is running in a loop, within each loop all the try blocks will be executed in orders (in which order doesn't matter), and I want to test how the code react to exception for each try/catch, one by one. For example, the first time in the loop, #1 try/catch block throws an exception; second time in the loop, #2 try/catch block throws an exception, etc etc.
I plan to have a global counter like this:
int g_testThrowExceptionIndex = 0;
In each try/catch:
try
{
TEST_THROW_EXCEPTION(__COUNTER__)
//My logic is here...
}
catch(...)
{
//Log or notify...
}
And the Macro would be something like this:
#define TEST_THROW_EXCEPTION(n) \
if(g_testThrowExceptionIndex == n)\
{\
g_testThrowExceptionIndex++;\
throw g_testThrowExceptionIndex;\
}\
Without the ability to generate the sequence number at compile time, I have to write the Macro like this:
TEST_THROW_EXCEPTION(THROW_INDEX_1)
......
TEST_THROW_EXCEPTION(THROW_INDEX_N)
And in the header, defines:
#define THROW_INDEX_1 0
#define THROW_INDEX_2 1
......
The problem is, every time you add a try/catch block and you want to test, you have to create a new constant through #define and put that number into the Macro. Worse, what if you remove some of the try/catch blocks from the code? You have to update your #define list too...
==============
Solution:
Thanks for Suma's idea, I ended up with something like this:
#if defined(_DEBUG) && defined(_EXCEPTION_TEST)
extern int g_testThrowExceptionIndex;
struct GCounter
{
static int counter; // used static to guarantee compile time initialization
static int NewValue() {return counter++;}
};
#define TEST_THROW_EXCEPTION \
static int myConst = GCounter::NewValue();\
if(g_testThrowExceptionIndex == myConst)\
{\
g_testThrowExceptionIndex++;\
throw 0;\
}
#else
#define TEST_THROW_EXCEPTION
#endif
In main.cpp:
#if defined(_DEBUG) && defined(_EXCEPTION_TEST)
int g_testThrowExceptionIndex= 0;
int GCounter::counter= 0;
#endif
Then you can put "TEST_THROW_EXCEPTION" in any of your try/catch block you want to test out.
You cannot do this using preprocessor, as each compile unit is preprocessed separately. A run time solution is needed for this. You may create a global singleton and each place which requires a unique identifier can define a static int using this singleton.
struct GCounter
{
static int counter; // used static to guarantee compile time initialization
static int NewValue() {return counter++;}
};
int GCounter::counter = 0;
void Foo1()
{
static int ID1 = GCounter::NewValue();
}
void Foo2()
{
static int ID2 = GCounter::NewValue();
}
Note: the order of initialization of those static values (IDs) in multiple compilation units is not defined. You can be sure they will be always unique, but you cannot rely upon them having some particular values or ordering. Therefore be careful when e.g. saving them into a file - you should translate them to some neutral representation for that.
Seeing as you are using MSVC, you can always add a pre-build step that parses over the files and expands __COUNTER__ to a super global value instead of a unit global value. Of course the hard part is managing the files so as not to cause problems...
i'd like to write a wrapper for a C++ framework. this framework is kinda buggy and not really nice and in C++. so i'd like to be able to call their methods from outside (via good old C file) of their framework by using just one shared lib. this sounds like the need for a wrapper that encapsulates the wanted framework methods for usage with C instead of C++.
So far so good.... here is what i already did:
interface aldebaran.h
(this is in my include folder, the ultrasound methods should be called from outside of the framework):
#ifndef _ALDEBARAN_H
#define _ALDEBARAN_H
#ifdef __cplusplus
extern "C" {
#endif
void subscribe_ultrasound();
void unsubscribe_ultrasound();
float read_ultrasound();
#ifdef __cplusplus
}
#endif
#endif
now the wrapper:
cpp file aldebaran.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aldebaran.h"
#include "alproxy.h"
#include "../../include/aldebaran.h"
/*
* Ultrasound defines
*/
#define ULTRASOUND_RESERVATION_MAGIC "magic_foobar"
#define ULTRASOUND_POLL_TIME 250
#define ULTRASOUND_READ_ATTEMPTS 50
#define ULTRASOUND_SLEEP_TIME 20
using namespace std;
using namespace AL;
/*
* Framework proxies
*/
ALPtr<ALProxy> al_tts;
ALPtr<ALProxy> al_led;
ALPtr<ALProxy> al_motion;
ALPtr<ALProxy> al_mem;
ALPtr<ALProxy> al_us;
ALPtr<ALProxy> al_cam;
ALPtr<ALProxy> al_dcm;
/*
* Constructor
*/
Aldebaran::Aldebaran(ALPtr<ALBroker> pBroker, std::string pName): ALModule(pBroker, pName)
{
try {
al_tts = this->getParentBroker()->getProxy("ALTextToSpeech");
al_led = this->getParentBroker()->getProxy("ALLeds");
al_motion = this->getParentBroker()->getProxy("ALMotion");
al_mem = this->getParentBroker()->getProxy("ALMemory");
al_us = this->getParentBroker()->getProxy("ALUltraSound");
al_cam = this->getParentBroker()->getProxy("NaoCam");
al_dcm = this->getParentBroker()->getProxy("DCM");
}catch(ALError& err){
std::cout << "XXX: ERROR: " << err.toString() << std::endl;
return 1;
}
printf("XXX: module aldebaran initiated\n");
fflush(0);
}
/*
* Destructor
*/
Aldebaran::~Aldebaran()
{
printf("XXX: module aldebaran destructed\n");
fflush(0);
}
/*
* Subscribe to ultrasound module
*/
void subscribe_ultrasound()
{
ALValue param;
param.arrayPush(ULTRASOUND_POLL_TIME);
al_us->callVoid("subscribe", string(ULTRASOUND_RESERVATION_MAGIC), param);
printf("XXX: ultrasound subscribed: %s\n", ULTRASOUND_RESERVATION_MAGIC);
fflush(0);
}
/*
* Unsubscribe to ultrasound module
*/
void unsubscribe_ultrasound()
{
al_us->callVoid("unsubscribe", string(ULTRASOUND_RESERVATION_MAGIC));
printf("XXX: ultrasound unsubscribed: %s\n", ULTRASOUND_RESERVATION_MAGIC);
fflush(0);
}
/*
* Read from ultrasound module
*/
float read_ultrasound()
{
int i;
float val1, val2;
float val_sum;
ALValue distance;
val_sum = .0f;
for(i = 0; i < ULTRASOUND_READ_ATTEMPTS; ++i){
SleepMs(ULTRASOUND_SLEEP_TIME);
distance = al_mem->call<ALValue>("getData", string("extractors/alultrasound/distances"));
sscanf(distance.toString(AL::VerbosityMini).c_str(),"[%f, %f, \"object\"]", &val1, &val2);
val_sum += val1;
}
return val_sum / (1.f * ULTRASOUND_READ_ATTEMPTS);
}
definition file for aldebaran.cpp:
#ifndef ALDEBARAN_API_H
#define ALDEBARAN_API_H
#include <string>
#include "al_starter.h"
#include "alptr.h"
using namespace AL;
class Aldebaran : public AL::ALModule
{
public:
Aldebaran(ALPtr<ALBroker> pBroker, std::string pName);
virtual ~Aldebaran();
std::string version(){ return ALTOOLS_VERSION( ALDEBARAN ); };
bool innerTest(){ return true; };
};
#endif
So this should be a simple example for my wrapper and it compiles fine to libaldebaran.so.
now my test program in C:
... now i'd like to call the interface aldebaran.h methods from a simple c file like this:
#include <stdio.h>
/*
* Begin your includes here...
*/
#include "../include/aldebaran.h"
/*
* End your includes here...
*/
#define TEST_OKAY 1
#define TEST_FAILED 0
#define TEST_NAME "test_libaldebaran"
unsigned int count_all = 0;
unsigned int count_ok = 0;
const char *__test_print(int x)
{
count_all++;
if(x == 1){
count_ok++;
return "ok";
}
return "failed";
}
/*
* Begin tests here...
*/
int test_subscribe_ultrasound()
{
subscribe_ultrasound();
return TEST_OKAY;
}
int test_unsubscribe_ultrasound()
{
unsubscribe_ultrasound();
return TEST_OKAY;
}
int test_read_ultrasound()
{
float i;
i = read_ultrasound();
return (i > .0f ? TEST_OKAY : TEST_FAILED);
}
/*
* Execute tests here...
*/
int main(int argc, char **argv)
{
printf("running test: %s\n\n", TEST_NAME);
printf("test_subscribe_ultrasound: \t %s\n", __test_print(test_subscribe_ultrasound()));
printf("test_read_ultrasound: \t %s\n", __test_print(test_read_ultrasound()));
printf("test_unsubscribe_ultrasound: \t %s\n", __test_print(test_unsubscribe_ultrasound()));
printf("test finished: %s has %u / %u tests passed\n\n", TEST_NAME, count_ok, count_all);
return (count_all - count_ok);
}
how can i manage to call these methods? i mean within my C file i have no possibility to create such an object-instance (that generated all the needed ALProxies), have i?
help would be really appreciated... thx
thank you very much so far!!
as xtofl said.. i'd like to keep my interface as simple as possible (without another c++ object preferably):
#ifndef _ALDEBARAN_H
#define _ALDEBARAN_H
#ifdef __cplusplus
extern "C" {
#endif
void subscribe_ultrasound();
void unsubscribe_ultrasound();
float read_ultrasound();
#ifdef __cplusplus
}
#endif
#endif
the problem hereby is that functions like subscribe_ultrasound() cannot be called without the instanciation of all the proxies... this is our precondition:
...
al_tts = this->getParentBroker()->getProxy("ALTextToSpeech");
al_led = this->getParentBroker()->getProxy("ALLeds");
al_motion = this->getParentBroker()->getProxy("ALMotion");
al_mem = this->getParentBroker()->getProxy("ALMemory");
al_us = this->getParentBroker()->getProxy("ALUltraSound");
al_cam = this->getParentBroker()->getProxy("NaoCam");
al_dcm = this->getParentBroker()->getProxy("DCM");
...
if i don't have the code above called, all other will fail.
within their framework it is possible to "autoload" my libaldebaran.so via a python script like this call:
myModule = ALProxy("Aldebaran", global_params.strRemoteIP, global_params.nRemotePort );
The framework log then says:
May 10 15:02:44 Hunter user.notice root: XXX: module aldebaran initiated
May 10 15:02:46 Hunter user.notice root: INFO: Registering module : 'Aldebaran'
May 10 15:02:46 Hunter user.notice root: ______ End of loading libraries ______
which is totally okay... it called the constructor of my module (so all other needed proxies got instanciated too).
but of course this instance does not belong to my C program...
maybe there is a possibility to share this to all other processes?
You might want to take a slightly different approach. Consider something like this for your C interface:
#ifdef __cplusplus
extern "C" {
#endif
struct UltrasoundHandle;
UltrasoundHandle* ultrasound_Create();
void ultrasound_Destroy(UltrasoundHandle *self):
void ultrasound_Subscribe(UltrasoundHandle *self);
void ultrasound_Unsubscribe(UltrasoundHandle *self);
float ultrasound_Read(UltrasoundHandle *self);
#ifdef __cplusplus
}
#endif
The UltrasoundHandle structure is purposefully opaque so that you can define it in the implementation to be whatever you want it to be. The other modification that I made was to add explicit creation and destruction methods akin to the constructor and destructor. The implementation would look something like:
extern "C" {
struct UltrasoundHandle {
UltrasoundHandle() {
// do per instance initializations here
}
~UltrasoundHandle() {
// do per instance cleanup here
}
void subscribe() {
}
void unsubscribe() {
}
float read() {
}
};
static int HandleCounter = 0;
UltrasoundHandle* ultrasound_Create() {
try {
if (HandleCounter++ == 1) {
// perform global initializations here
}
return new UltrasoundHandle;
} catch (...) {
// log error
}
return NULL;
}
void ultrasound_Destroy(UltrasoundHandle *self) {
try {
delete self;
if (--HandleCounter == 0) {
// perform global teardown here
}
} catch (...) {
// log error
}
}
The key is to wrapping C++ interfaces for C is to expose the OO concepts through free functions where the caller explicitly passes the object pointer (this) to the function and to explicitly expose the constructor and destructor in the same manner. The wrapper code can be almost mechanically generated from there. The other key points are that you never let exceptions propagate outward and steer clear of global object instances. I'm not sure if the latter will cause you grief, but I would be concerned about construction/destruction ordering problems.
You said yourself to create a C wrapper API around an OO framework.
This means you don't need any objects passing the wrapper API (as it appears from the decribed header). It seems all objects needed are created/destructed behind the wrapper API, out of view of your test program.
The first seems the case. You don't need objects to test your wrapper API. In the end, all objects are bytes (in memory) that are accessed through a fixed set of functions. It doesn't matter much whether the functions are written as member-functions (C++) or as plain C functions, as long as they obey the intended semantics of your objects.
I'm not clear whether you're aware of this, but if you have C++ code to dynamically load into your program, then you should link your program with the C++ compiler and make your main function a C++ function too - even if it is as trivial as:
int main(int argc, char **argv)
{
return(real_main_in_c(argc, argv));
}
The real_main_in_c() function is what you previously called main(); it has simply been renamed. This ensures that the C++ mechanisms for handling initialization of global and static variables, etc, are loaded and operational. The C++ startup code does more work than the C startup code. Dynamically loading C
This is only one (small) facet of the answer - but it is an important practical one.
Keep things as simple as possible - but not simpler. I think you're trying to make it too simple here, wrap it all up but use the c++ compiler.
so, if you create your wrapper using the c++ compiler, you can instantiate the objects inside your subscribe function, release them all in the unsubscribe, all using static (or global) objects. The 3 functions you want to expose simple get wrapped with "extern C" and you have a C-style interface exposed to any caller, whilst still encapsulating C++ objects.
If you need another function to instantiate all the proxies, add one; alternatively if they don't already exist, create them so they'll always be created in the first call to subscribe.
Now, if you need the proxy objects on a per-instance basis (ie you have 2 callers who both want to subscribe, and need a unique proxy per caller), then you'll have to store the objects in a collection (I suggest a map), every call you make must then pass in a 'handle' or 'sessionid' that you use to extract the per-call objects from the map.