Out of curiosity, I thought I'd try and write a basic C++ class that mimics C#'s multiple delegate pattern. The code below mostly does the job, with the nasty sacrifice of losing almost all type-safety, but having to use the initial dummy parameter to set up the va_list really seems a bit off. Is there a way to use va_list without this?
I do realize there are ways to do this with (for example) boost, but I was aiming for something dead simple that used just the standard library.
#include <vector>
#include <iostream>
#include <string>
#include <stdarg.h>
#include <algorithm>
using namespace std;
class CDelegate
{
public:
virtual bool operator()(va_list params) = 0;
};
class CMultipleDelegateCaller
{
public:
typedef vector<CDelegate*> CDelegateVector;
CMultipleDelegateCaller& operator+=(CDelegate &rDelegate)
{
m_apDelegates.push_back(&rDelegate);
return (*this);
}
CMultipleDelegateCaller& operator-=(CDelegate &rDelegate)
{
CDelegateVector::iterator iter =
find(m_apDelegates.begin(), m_apDelegates.end(), &rDelegate);
if (m_apDelegates.end() != iter) m_apDelegates.erase(iter);
return (*this);
}
bool Call(int iDummy, ...)
{
va_list params;
CDelegate* pDelegate;
CDelegateVector::iterator iter;
for (iter = m_apDelegates.begin(); iter != m_apDelegates.end(); ++iter)
{
pDelegate = *iter;
va_start(params, iDummy);
if (!(*pDelegate)(params)) return false;
va_end(params);
}
return true;
}
private:
CDelegateVector m_apDelegates;
};
class CTestDelegate:
public CDelegate
{
public:
CTestDelegate():m_iId(++s_iCount) {}
virtual bool operator()(va_list params)
{
int iIntParam = va_arg(params, int);
char* szCharPtrParam = va_arg(params, char*);
string* psStringParam = va_arg(params, string*);
cout<<m_iId<<"{"
<<iIntParam<<", "
<<szCharPtrParam<<", "
<<*psStringParam<<"}"<<endl;
return true;
}
int m_iId;
static int s_iCount;
};
int CTestDelegate::s_iCount = 0;
int main(int argc, char* argv[])
{
CMultipleDelegateCaller cDelegateCaller;
CTestDelegate cTestDelegate1;
CTestDelegate cTestDelegate2;
cout<<"--------------------"<<endl;
cDelegateCaller += cTestDelegate1;
cDelegateCaller += cTestDelegate2;
string sString("World");
cDelegateCaller.Call(1, 2, "Hello", &sString);
cout<<"--------------------"<<endl;
cDelegateCaller -= cTestDelegate1;
cDelegateCaller.Call(1, 2, "Hello", &sString);
cout<<"--------------------"<<endl;
cDelegateCaller -= cTestDelegate2;
cDelegateCaller.Call(1, 2, "Hello", &sString);
cout<<"--------------------"<<endl;
cin>>sString;
return 0;
}
Functions with ellipsis in C++ is only for compatibility with C. Using C++ I'd return temporary helper object in Call function and add template operator% to pass variable number of arguments. To use it in the following way:
cDelegateCaller.Call() % 2 % "Hello" % sString; // dummy argument isn't required
As to your question, Standard requires to invoke va_start before any access to the unnamed arguments. And va_start requires second argument which is the identifier of the rightmost parameter in the variable parameter list in the function definition.
Out of Kirill's answer you can conclude that it's possible to create a type-safe delegate, using a template argument-combining function. This function also needs a dummy starting point, but has the benefit of type-safety.
The FastFormat library uses this, boost uses this, and I once provided another example in an answer to another question.
Related
I'm trying to write something similar to this:
void loadImage(SDL_Renderer *ren, {SDL_Texture *&texture, const char *imagePath} ... ){
for(int i = 0; i < numOfPairs; i++){
SDL_Surface *curImage = IMG_Load(pairs[i].second);
pairs[i].first = SDL_CreateTextureFromSurface(ren, curImage);
SDL_FreeSurface(curImage);
}
}
Where I have a variable number of pairs and each pair contains a texture and its correspondent path. I've no idea, which way would be best to approach this problem. I've thought about using the <cstdarg> library's variadic functions but I read on another question that that's not a good solution.
the way that immediately comes to mind is std::vector. You could pass a vector of std::pair or of std::tuple if you are using a c++11 or better compiler.
very simple to do
#include <vector>
#include <tuple>
....
std::vector<std::tuple<SDL_texture, const char *>> args;
args.push_back(std::make_tuple(texture1, path1));
args.push_back(std::make_tuple(texture2, path2));
then your func
void load_Image(SDL_renedred *ren, const std::vector<std::tuple<SDL_texture, const char *>> &args)
{
std::tuple<SDL_textture, const char*> &arg1 = args[0];
// or if you have modern c++
auto &arg1 = args[0];
}
(not compiled, probably got typos in it.)
Although I hate it, you can use std::initializer_list like:
#include <initializer_list>
#include <tuple>
void loadImage(SDL_Renderer *ren, std::initializer_list<std::tuple<SDL_Texture*&,const char *&>> pair_list ){
for(auto p:pairlist){//initializer_list cannot be indexed but only iterated
SDL_Surface *curImage = IMG_Load(p.second);
p.first = SDL_CreateTextureFromSurface(ren, curImage);
SDL_FreeSurface(curImage);
};
};
//call site:
loadImage(ren,{tie(tex1,path1),tie(tex2,path2),tie(tex3,path3)});
It is recommended to store your pointers in std::unique_ptr instants with costume deleter. And I guess you'd better change your data structue design to avoid tying objects like that - e.g. using a std::map:
#include <initializer_list>
#include <memory>
#include <map>
struct MySDLDeleter{
void operator(SDL_Texture* texPtr){SDL_FreeTexture(texPtr);};
void operator(SDL_Renderer * renPtr){SDL_FreeRenderer(renPtr);};
void operator(SDL_Surface * srfPtr){SDL_FreeSurface(srfPtr);};
};
auto loadImage(std::unique_ptr<SDL_Renderer,MySDLDeleter> ren, std::initializer_list<const char *> path_list ){
std::map<const char *,std::unique_ptr<SDL_Texture,MySDLDeleter> texMap;
for(auto p:path_list ){
std::unique_ptr<SDL_Surface ,MySDLDeleter> curImage = IMG_Load(p.second);
texMap.insert(p,SDL_CreateTextureFromSurface(ren.get(), curImage.get()));
};
return texMap;
};
//call site:
auto texMap=loadImage(create_the_renderer_ptr(),{path1,path2,path3});
First of all write a function that will process each pair (i.e. what you have inside your for loop):
using ImagePair = std::pair<SDL_Texture*&, const char*>;
void processImage(ImagePair imgPair)
{
SDL_Surface *curImage = IMG_Load(imgPair.second);
imgPair.first = SDL_CreateTextureFromSurface(ren, curImage);
SDL_FreeSurface(curImage);
}
Then if you have C++11 or above, you can use a brace-initialization trick to call processImage() for each argument:
template <typename ... Ts>
void loadImage(Ts ... ts)
{
using dummy = int[];
(void)dummy {0, (processImage(ts)), 0)... };
}
What you're doing here is taking advantage of the fact that the compiler knows it has to do a pack expansion inside a brace-initialization list. That way you can avoid having to write recursive variadic template functions yourself.
Here you are building a dummy array of integers, which is equal in size to the number of variadic arguments you pass in. For each argument you call the function processImage(), but using the comma operator you set the value in the array to 0. So you expand the pack and call processImage() for each argument while creating a dummy array. This answer might explain it more clearly.
If you have C++17 or above, you can simplify further and use a fold expression:
template<typename... Ts>
void loadImage(Ts... args)
{
(processImage(args),...);
}
I'm trying to implement a class (C++) with an enum (with the permitted parameters). I got a working solution, but if I try to extend the functionality I get stuck.
Header data_location.hpp
class DataLocation
{
private:
public:
enum Params { model, period };
std::string getParamString(Params p);
};
Program data_location.cpp
string DataLocation::getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
The array ParamsStrings should be generally available in the class, because I need a second method (with inverse function) returning the enum value given a string.
If I try to define the array in the header I get the error:
in-class initialization of static data member ‘const char* DataLocation::ParamsStrings []’ of incomplete type
Why is the type incomplete? The compiler is for sure able to counts the strings in the array, isn't it?
In case there is no way to get my code working, is there an other way? With 1) no XML; 2) no double definition of the strings; 3) not outside the class; 4) no in code programmed mapping.
In class (header) use keyword static and initialize it outside (.cpp) without the static keyword:
class DataLocation {
public:
enum Params { model, period };
string getParamString(Params p);
static const char* ParamsStrings[];
// ^^^^^^
};
const char* DataLocation::ParamsStrings[] = {"MODEL", "BLLBLA"};
//^^^^^^^^^^^^^^^^^^^^^^^^
The code you have posted is perfectly fine.
Here's the proof:
#include <iostream>
#include <string>
struct DataLocation
{
enum Params { model, period };
std::string getParamString(Params p){
static const char * ParamsStrings[] = {"MODEL", "PERIOD"};
return ParamsStrings[p];
}
};
int main()
{
auto a = DataLocation();
std::cout << a.getParamString(DataLocation::model) << std::endl;
return 0;
}
The error message you are getting is not to do with definition of a static data member in an inline function - that's allowed.
There's something else you're not showing us.
The main issue in my question (the second part) was that if I split the class in .hpp and .cpp the definition of the array (I mixed *char and string) has also to be split:
// data_location.hpp
class DataLocation {
static const char * ParamsStrings[];
}
// data_location.cpp
const char * ParamsStrings[] = {"MODEL", "PERIOD"};
At the end I introduced a consistency check to be sure that the number of values in enum growths as the number of strings. Because the array in C++ is somehow limited I had to go for a std::vector (to get the size).
Code for data_location.hpp
#ifndef DATA_LOCATION_HPP_
#define DATA_LOCATION_HPP_
#include <string>
#include "utils/dictionary.hpp"
extern const char* ENV_DATA_ROOT;
struct EDataLocationInconsistency : std::runtime_error
{
using std::runtime_error::runtime_error;
};
struct EDataLocationNotValidParam : std::runtime_error
{
using std::runtime_error::runtime_error;
};
class DataLocation
{
private:
std::string mRootLocation;
static const std::vector<std::string> msParamsStrings;
static bool msConsistenceCheckDone;
public:
DataLocation();
std::string getRootLocation();
std::string getLocation(Dictionary params);
enum Params { model, period, LAST_PARAM};
std::string Param2String(Params p);
Params String2Param(std::string p);
};
#endif
Code for data_location.cpp
#include "data_location.hpp"
#include <string>
#include <cstdlib>
using namespace std;
const char* ENV_DATA_ROOT = "DATA_ROOT";
bool DataLocation::msConsistenceCheckDone = false;
DataLocation::DataLocation() {
mRootLocation = std::getenv(ENV_DATA_ROOT);
if (not msConsistenceCheckDone) {
msConsistenceCheckDone = true;
if (LAST_PARAM+1 != msParamsStrings.size()) {
throw(EDataLocationInconsistency("DataLocation: Check Params and msParamsStrings"));
}
}
}
string DataLocation::getRootLocation() {
return mRootLocation;
}
string DataLocation::getLocation(Dictionary params) {
// to do
return "";
}
const vector<string> DataLocation::msParamsStrings = { "MODEL", "PERIOD", ""};
string DataLocation::Param2String(Params p) {
if (p>=msParamsStrings.size()) {
throw(EDataLocationNotValidParam("Parameter not found"));
}
return msParamsStrings[p];
}
DataLocation::Params DataLocation::String2Param(string p) {
for (int i = 0; i < msParamsStrings.size(); i++) {
if (p == msParamsStrings[i])
return (Params)i;
}
throw(EDataLocationNotValidParam("Parameter not found"));
}
And also a unit test:
#include <boost/test/unit_test.hpp>
#include "data_location.hpp"
#include <string>
using namespace std;
BOOST_AUTO_TEST_SUITE( data_location )
BOOST_AUTO_TEST_CASE(data_location_1) {
DataLocation dl;
auto s = dl.getRootLocation();
BOOST_CHECK_EQUAL(s, "/home/tc/data/forex" );
BOOST_CHECK_EQUAL(dl.Param2String(DataLocation::period),"PERIOD");
BOOST_CHECK_EQUAL(dl.String2Param("PERIOD"),DataLocation::period);
BOOST_CHECK_THROW(dl.String2Param("SOMETHING"), EDataLocationNotValidParam);
BOOST_CHECK_THROW(dl.Param2String((DataLocation::Params)100), EDataLocationNotValidParam);
}
BOOST_AUTO_TEST_SUITE_END()
C++ is very picky about what it will let you initialize inside of a class definition; there are some particularly non-intuitive rules surrounding static members. It all has to do with the ODR, and why all the rules are the way they are is not especially important.
To cut to the chase, making your array a static constexpr const member should shut the compiler up. With the C++11 standard, the restrictions were relaxed a bit, and one of the new stipulations was that static constexpr members can be initialized inline. This is perfect for your application, since the strings in your array are compile-time constants.
The recent g++ compiler which support C++0x or later compiles thus code. Pure C compile compiles, too. Because strings in initialization like {"MODEL", "PERIOD"}; implemented as const char * pointer to the char array.
So this is confusing to explain, but I will try my best.
I have a function one of my classes that takes a function pointer as an argument, and what I would like to do is define the function as part of the argument. ie:
object->setFunctionPointer({string a = ""; return a;});
Is this possible? if so, what is the proper syntax of this?
In C++11, you can do it. You can use C++ lambda (anonymous functions).
See the sample code at http://ideone.com/8ZTWSU
#include <iostream>
using namespace std;
typedef const char * (*funcptr)();
funcptr s;
void setFuncPtr(funcptr t)
{
s = t;
}
int main() {
// your code goes here
setFuncPtr([]{return "Hello \n"; });
printf("%s\n", s());
return 0;
}
If we are talking about C++ you should use std::function and not function pointers. Unless you are interfacing with C APIs.
class Foo{
SetFunc(std::function<void(int)> func)
{
m_func = func;
}
private:
std::function<void(int)> m_func;
};
If your function is a member of a class, you cannot take an ordinary function pointer to store its address. What you need is a delegate; which are specialised function pointers for methods. Search the internet for C++ delegate and you should find numerous examples.
(Note: maybe there is an exception for static methods; I don't remember.)
Here is a complete example. Since c++11 this is the way to go:
#include<functional>
#include<string>
#include<iostream>
using namespace std;
class Object
{
public:
void setFunctionPointer(function<string(void)> function)
{
m_function = function;
}
string run()
{
return m_function();
}
private:
function<string(void)> m_function;
};
int main(int argc, char**argv)
{
Object *object = new Object;
object->setFunctionPointer([]{string a = "FOO"; return a;}); // here is the function assignment
cout << object->run() << endl;
delete object;
}
When run this prints FOO to stdout.
I'm trying to make a class that can hold and later call functions. It stores the functions in a map along with a string that holds the name of the function.
I tried doing this on Linux with GCC and got the following error: "invalid conversion from void(*)() to void *" on the line functionsMap[nameOfFunction] = func;
Here's the entire program I have so far. It's not done yet, but I'm really curious as to why this would compile under Visual C++ and not GCC. If I'm doing something wrong or could be doing something better, please let me know. Thanks!
#include <iostream>
#include <map>
#include <string>
using namespace std;
class Dyn_Class{
private:
map<string, void *> functionsMap;
public:
Dyn_Class(){}
template<typename ReturnValue>
void add_func( string nameOfFunction, ReturnValue(*func)() ){
functionsMap[nameOfFunction] = func;
}
void remove_func( string nameOfFunction ){
}
Dyn_Class operator()(string nameOfFunction){
}
};
void print(void){
for(int index = 0; index < 9; index++){
cout << index << " ";
}
cout << endl;
}
int main(){
Dyn_Class functionsList;
functionsList.add_func("print", print);
return 0;
}
To have a map of pointers to function taking no arguments and returning void you need:
std::map<std::string, void(*)()> functionsMap;
There is no point making add_func a template as it will only work when instantiated with ReturnValue = void (unless you add a potentially unsafe cast to its implementation).
If your code compiles with Visual C++ it is because Visual C++ is being permissive.
You can pass that function as a parameter like this:
void add(void * f()){...}
How do you pass a function as a parameter in C?
Think on using std::function instead:
class Dyn_Class{
private:
map<string, function<void()> > functionsMap;
public:
Dyn_Class(){}
template<typename FUNC>
void add_func(const string& nameOfFunction, FUNC func){
functionsMap.insert(make_pair(nameOfFunction, func));
}
void remove_func(const string& nameOfFunction ){
}
void operator()(const string& nameOfFunction){
functionsMap[nameOfFunction]();
}
};
Benefits? Using "function", you could use your plain old function pointers, you can use functors or you can use lambda expressions instead:
DynClass dyn;
dyn.add("print", []() { printf("Say hi"; } );
This question already has answers here:
Is it possible to print a variable's type in standard C++?
(25 answers)
Closed 5 years ago.
While playing with templates in c++ I encountered a problem converting typename T to string. For example:
template <typename T>
class Matrix {
public:
Matrix() {
//my_type = string type of T. i.e. if T is char. I want my_type to be "char".
}
string my_type;
}
How do I convert T to a string that says what T is.
Note: I'm just playing around so please do not worry about when one might need such a thing.
There is no built-in mechanism for this.
typeid(T)::name() can give some info, but the standard does not mandate this string to be human-readable; just that it has to be distinct for each type. (E.x. Microsoft Visual C++ uses human-readable strings; GCC does not.)
You can build your own system though. For example, traits-based. Something like this:
// default implementation
template <typename T>
struct TypeName
{
static const char* Get()
{
return typeid(T).name();
}
};
// a specialization for each type of those you want to support
// and don't like the string returned by typeid
template <>
struct TypeName<int>
{
static const char* Get()
{
return "int";
}
};
// usage:
const char* name = TypeName<MyType>::Get();
For GCC you have to use a trick. Using cxxabi.h, I wrote a little wrapper for this purpose:
#include <string>
#include <iostream>
#include <iomanip>
#include <typeinfo>
#include <cxxabi.h>
#define DEBUG_TYPE(x) do { typedef void(*T)x; debug_type<T>(T(), #x); } while(0)
template<typename T>
struct debug_type
{
template<typename U>
debug_type(void(*)(U), const std::string& p_str)
{
std::string str(p_str.begin() + 1, p_str.end() - 1);
std::cout << str << " => ";
char * name = 0;
int status;
name = abi::__cxa_demangle(typeid(U).name(), 0, 0, &status);
if (name != 0) { std::cout << name << std::endl; }
else { std::cout << typeid(U).name() << std::endl; }
free(name);
}
};
The double parentheses are necessary. Will work with any type.
Now you can use it for boost::mpl:
DEBUG_TYPE((if_c<true, true_, false_>::type));
will print:
if_c<true, true_, false_>::type => bool_<true>
You can't, at least not directly. The only way to convert a token or series of tokens into a string literal is using the preprocessor's stringization operator (#) inside of a macro.
If you want to get a string literal representing the type, you'll have to write something yourself, perhaps by using a macro to instantiate the template and pass it the stringized type name.
One problem with any general approach is: what string should be given for the following uses:
Matrix<char> x;
typedef char MyChar;
Matrix<MyChar> y;
Both x and y are of the same type, but one uses char directly and the other uses the typedef MyChar.
It is impossilbe to get name of type in string if the type is one of base types. For user defined types you can use typeid(my_type).name(). Also you need #include <typeinfo> :)
more info...
workaround way...
#define Tprint(x) print<x>(#x)
template<typename T>
void print (string ltype){
cout<<ltype<<" = "<<sizeof(T)<<endl;
}
You could use a C++ reflection library. So:
using namespace ponder;
Class::declare<Matrix>();
std::string const& name = classByType<Matrix>().name();
This gives you other options as well once you have the metaclass information, like looking what the class members are.
template< typename From,typename To>
static inline bool superConvert(const From& fromVar,To& toVar)
{
stringstream ss;
ss<<fromVar;
ss>>toVar;
if(ss.fail())
{
return false;
}
else
{
From tempFrom;
stringstream ss;
ss<<toVar;
ss>>tempFrom;
if(tempFrom != fromVar)
{
return false;
}
else
{
return true;
}
}
}