I have a map of callbacks that pass information and execute various functions throughout code, very much like events in C# but in C++.
The map is defined as
std::map<std::string, std::function<void(uint8_t*)>> mCallbacks
It is passed by reference to all subprograms
Then each class binds its callbacks as such
mCallbacks["Status_Label"] = std::bind(&MenuHandler::LabelEditCallback, this, std::placeholders::_1);
Where
bool MenuHandler::LabelEditCallback(uint8_t * m_label_data)
{
int text_size = ((int*)m_label_text)[0];
char* v_text = (char*)&m_label_text[1];
}
And each event gets called from a different subprogram like this:
if (mCallbacks.find("Status_Label") != mCallbacks.end())
mCallbacks.at("Status_Label")((uint8_t*)selected_text);
This makes it easy to pass data and events around the program without making a mess of objects and references
As you can see, this is extremely unsafe, and converting from a uint8_t pointer to various data formats can easily lead to corrupted stack.
The problem is, I don't have a specific structure for callback arguments, some of them may be sending text data, others may be sending numbers.
My solution is to define structs that will be cast to void* when calling the event, and back in the callback function
Something like this (untested):
struct Label_Callback_Data
{
Label_Callback_Data(std::string v_name, std::string v_text)
{
labelName = v_name;
labelText = v_text;
size_of = sizeof(this);
}
int size_of;
std::string labelName;
std::string labelText;
};
And I would call it like this:
if (mCallbacks.find("Status_Label") != mCallbacks.end())
mCallbacks.at("Status_Label")((uint8_t*)Label_Callback_Data("Status_Label_name", "TEXT"))
But then how would I recover it here? If I dont know the exact size of the object?
bool MenuHandler::LabelEditCallback(uint8_t * m_label_data)
{
//?? Label_Callback_Data text_size = (Label_Callback_Data*)m_label_text
}
One solution is to use object with fixed size arrays, but there has to be a C++11 solution that is safe to use, maybe something using dynamic_pointer_casts?
Also, as a bonus question, how would I know if the object passed to the callback function is smaller in size than it is expecting? Is it possible to check this and just return a false from the callback function so the program doesn't crash?
Thank you,
This code is not tested, so there may be logical mistakes I'm willing to correct per responses.
You should generally prefer to use a lambda instead of std::bind().
Try something more like this:
std::map<std::string, std::function<void(void*)>> mCallbacks;
struct Label_Callback_Data
{
std::string labelName;
std::string labelText;
Label_Callback_Data(std::string v_name, std::string v_text)
: labelName(v_name), labelText(v_text) { }
};
...
mCallbacks["Status_Label"] = [this](void *data){ this->LabelEditCallback(data); };
...
auto iter = mCallbacks.find("Status_Label");
if (iter != mCallbacks.end())
{
Label_Callback_Data data("Status_Label_name", "TEXT");
iter->second(&data);
}
...
bool MenuHandler::LabelEditCallback(void *m_label_data)
{
Label_Callback_Data *data = static_cast<Label_Callback_Data*>(m_label_text);
// use data->labelName and data->labelText as needed...
}
Alternatively, you could move the type-cast into the lambda itself, so LabelEditCallback() doesn't need to deal with void* at all:
std::map<std::string, std::function<void(void*)>> mCallbacks;
struct Label_Callback_Data
{
std::string labelName;
std::string labelText;
Label_Callback_Data(std::string v_name, std::string v_text)
: labelName(v_name), labelText(v_text) { }
};
...
mCallbacks["Status_Label"] = [this](void *data){ this->LabelEditCallback(static_cast<Label_Callback_Data*>(data)); };
...
auto iter = mCallbacks.find("Status_Label");
if (iter != mCallbacks.end())
{
Label_Callback_Data data("Status_Label_name", "TEXT");
iter->second(&data);
}
...
bool MenuHandler::LabelEditCallback(Label_Callback_Data *m_label_data)
{
// use m_label_data->labelName and m_label_data->labelText as needed...
}
This is how I did it
...
//The container
std::map<std::string, std::function<void(std::shared_ptr<CallbackData::BlankData>)>> mCallbacks
...
//CALLBACK FUNCTION
bool InputManager::KeyboardCallback(std::shared_ptr<CallbackData::BlankData> callback_data)
{
std::shared_ptr<CallbackData::Keyboard> keyboard_data = std::dynamic_pointer_cast<CallbackData::Keyboard>(callback_data);
if (keyboard_data == nullptr)
return false;
///...
}
...
//CALLBACK EVENT
if (mCallbacks.find("Keyboard") != mCallbacks.end())
{
std::shared_ptr<CallbackData::Keyboard> keyboard_data = std::make_shared<CallbackData::Keyboard>(m_keyboardState);
mCallbacks.at("Keyboard")(std::dynamic_pointer_cast<CallbackData::BlankData>(keyboard_data));
}
...
//Data structure
namespace CallbackData
{
struct BlankData
{
virtual ~BlankData() {};
};
struct Keyboard : public BlankData
{
Keyboard(uint8_t* kb_data)
{
kbData = kb_data;
}
uint8_t* kbData;
};
}
Related
I have this function where I need to return const std::list<Album> , and what I have is std::list<Album*>*.
How can I convert the first type to the second type?
const std::list<Album> DatabaseAccess::getAlbums()
{
char* sqlStatement = "SELECT * FROM Albums;";
char* errMessage = nullptr;
std::list<Album*>* data = new std::list<Album*>;
sqlite3_exec(this->_DataBase, sqlStatement, Album::callback, data, &errMessage);
return ; // ?
}
EDIT:
Will this work?
std::list<Album> casted_list;
for (auto i = data->begin(); i != data->end(); i++)
{
casted_list.push_back(*(*i));
}
It looks like you are using a lot of unnecessary pointers and dynamic allocation in your code. It is hard to be sure without seeing what your Album objects look like but I would probably do something like this:
class Album
{
public:
Album(std::string const& name): m_name(name) {}
static int callback(void* uptr, int no_of_cols, char** results, char** column_names)
{
// cast our void* to what we are working with
std::list<Album>& albums = *reinterpret_cast<std::list<Album>*>(uptr);
// pass the Album's constructor arguments to emplace(...)
albums.emplace_back(results[0]);
return {};
}
private:
std::string m_name;
};
class DatabaseAccess
{
public:
std::list<Album> getAlbums();
private:
sqlite3* _DataBase = nullptr;
};
std::list<Album> DatabaseAccess::getAlbums()
{
char const* sqlStatement = "SELECT * FROM Albums;";
char* errMessage = nullptr;
// No need to get into pointers here
std::list<Album> data;
// send the address of data to the callback function
sqlite3_exec(this->_DataBase, sqlStatement, Album::callback, &data, &errMessage);
return data; // then just return your list
}
As far as I can tell you don't really need to create anything other than a std::list<Album> (your desired return type) to begin with and avoid all the pointers.
I'm trying to create a map of strings to functions. When it's a simple function, I've seen how to do this like so:
typedef int (*GetIntFunction)(void);
int get_temp()
{
return 42;
}
map<string, GetIntFunction> get_integer_map { { "temp", &get_temp } };
auto iter = get_integer_map.find("temp");
int val = (*iter->second()();
However, I'd like my function pointer to be to a function of a specific object. And, I know which object I need at map creation. Something like this:
class TemperatureModel
{
public:
int GetTemp();
}
TemperatureModel *tempModel = new TemperatureModel();
map<string, GetIntFunction> get_integer_map { { "temp", &(tempModel->GetTemp} };
If you'd like to know why I'm doing this, I'm trying to read a list of parameters from an input file, get their values from the correct model, and then output their values to an output file. I will also need to set values at runtime using a similar map.
The simplest approach to us old-fashioned types is to write a function:
int call_it() {
return tempModel->GetTemp();
}
and store that function in the map in the question:
map<string, GetIntFunction> get_integer_map {
{ "temp", call_it }
};
A newer approach is to use a lambda:
map<string, GetIntFunction> get_integer_map {
{ "temp", [=]() { return tempModel->GetTemp(); }
};
Another approach (as suggested in the comment by Kamil Cuk) is to use std::function to bind the object and the function:
map<string, std::function<int()>> get_integer_map {
{ "temp", std::function<int()>(&TemperatureModel::GetTemp, tempModel) }
};
Caution: code written but not compiled; it may have errors.
I am getting the error term does not evaluate to a function taking 1 arguments when trying to call a function pointer.
The function pointer is stored in a struct. The struct is then stored in a map.
Definition:
typedef void (CLIOptions::*OptionHandler)(QString);
struct OptionDefinition {
QString name;
QString description;
QString type;
OptionHandler handler;
};
typedef std::map<QString, OptionDefinition> CLIOptionMap;
I initialise the map like this:
CLIOptionMap optionMap =
{
{
QString("set-tcp-host"),
{
QString("set-tcph"),
QString("Set the TCP server host address"),
QString("string"),
&CLIOptions::setTcpHost
}
},
// etc...
}
The problem occurs when I try to iterate through the map and call the handler:
for (it = optionMap.begin(); it != optionMap.end(); ++it) {
QString value = /*snip...*/
(it->second.handler)(value)
}
What is my problem here?
Your problem is that you don't have a function pointer, you have a pointer to member function, and they are very different beasts. A pointer-to-member-function isn't even a pointer in general (it has to be able to handle pointer to a virtual function in a second virtual base class!)
Given you have a pmf, you need an object to invoke the pmf on. So something like:
for (it = optionMap.begin(); it != optionMap.end(); ++it) {
QString value = /*snip...*/
const auto pmf = it->second.handler;
(mOptionHandler.*pmf)(value);
}
actually, if you going to use C++11 auto, you can also use the foreach loop:
for (const auto& option : optionMap) {
const auto pmf = option.handler;
(mOptionHandler.*pmf)(option.value);
}
I want to write a class that can monitor a bunch of different values for easy debugging. Imagine setting "watches" in a visual debugger. I'm picturing something like this:
struct Foo {
int x = 0;
std::string s = "bar";
};
int main() {
Foo f;
ValueMonitor::watch("number", &f.x);
ValueMonitor::watch("string", &f.s);
for (int i = 0; i < 10; ++i) {
++f.x;
if (i > 5) {
f.s = "new string";
}
// print the current value of the variable with the given key
// these should change as the loop goes on
ValueMonitor::print("number");
ValueMonitor::print("string");
// or
ValueMonitor::printAll();
// obviously this would be unnecessary in this example since I
// have easy access to f, but imagine monitoring different
// values from all over a much larger code base
}
}
Then these could be easily monitored somewhere in the application's GUI or whatever.
However, I don't know how to handle the different types that would be stored in this class. Ideally, I should be able to store anything that has a string representation. I have a few ideas but none of them really seem right:
Store pointers to a superclass that defines a toString function or operator<<, like Java's Object. But this would require me to make wrappers for any primitives I want to monitor.
Something like boost::any or boost::spirit::hold_any. I think any needs to be type casted before I can print it... I guess I could try/catch casting to a bunch of different types, but that would be slow. hold_any requires defined stream operators, which would be perfect... but I can't get it to work with pointers.
Anyone have any ideas?
I found a solution somewhere else. I was pretty blown away, so might as well post it here for future reference. It looks something like this:
class Stringable
{
public:
virtual ~Stringable() {};
virtual std::string str() const = 0;
using Ptr = std::shared_ptr<Stringable>;
};
template <typename T>
class StringableRef : public Stringable
{
private:
T* _ptr;
public:
StringableRef(T& ref)
: _ptr(&ref) {}
virtual ~StringableRef() {}
virtual std::string str() const
{
std::ostringstream ss;
ss << *_ptr;
return ss.str();
}
};
class ValueMonitor
{
private:
static std::map<std::string, Stringable::Ptr> _values;
public:
ValueMonitor() {}
~ValueMonitor() {}
template <typename T>
static void watch(const std::string& label, T& ref)
{
_values[label] = std::make_shared<StringableRef<T>>(ref);
}
static void printAll()
{
for (const auto& valueItr : _values)
{
const String& name = valueItr.first;
const std::shared_ptr<Stringable>& value = valueItr.second;
std::cout << name << ": " << value->str() << std::endl;
}
}
static void clear()
{
_values.clear();
}
};
std::map<std::string, Stringable::Ptr> ValueMonitor::_values;
.
int main()
{
int i = 5;
std::string s = "test"
ValueMonitor::watch("number", i);
ValueMonitor::watch("string", s);
ValueMonitor::printAll();
i = 10;
s = "new string";
ValueMonitor::printAll();
return 0;
}
The solution may be simple. Then again it may not be possible.
I have the base callback class:
class CFCallback {
int command_;
int transfer_rate_;
public:
CFCallback(int command, int transfer_rate = 0) {
command_ = command; transfer_rate_ = transfer_rate; }
virtual ~CFCallback() {}
virtual void operator()(void *data) = 0;
int GetCommand() { return command_; }
int GetTransferRate() { return transfer_rate_; }
};
And here's one example of deriving from CFCallback:
void CFPacketVersion::InitiateVersion() {
class InitiateVersionCB : public CFCallback {
CFPacketVersion *visitor_;
public:
InitiateVersionCB(CFPacketVersion *v, int command) :
CFCallback(command) {
visitor_ = v;
}
void operator()(void *data) {
Packet *pkt = (Packet *)data;
unsigned char *pkt_data = pkt->GetData();
std::string version = "";
for(unsigned int i = 0; i < pkt->GetDataLength(); i++ )
version+= pkt_data[i];
delete []pkt_data;
boost::regex rex("CFA(.*?):h(.*?),v(.*?)$");
boost::smatch what;
if( boost::regex_match(version, what, rex) ) {
if(visitor_->GetModel()->GetName() != what[1].str() )
LCDInfo("Crystalfontz: Model mismatch");
visitor_->SetHardwareVersion(what[2]);
visitor_->SetFirmwareVersion(what[3]);
}
}
};
GetVersion(new InitiateVersionCB(this, 1));
}
GetVersion(CFCallback *) is provided to the script engine.
I want to be able to do the same thing as seen in InitiateVersion, but on the javascript side of things. Is that possible?
I know I need to register meta type info for CFCallback. But I don't know if it's possible to use a pointer to a CFCallback. What I tried initially didn't work.
Also, seeing as CFCallback is a functor, I'm not sure how I translate that over to javascript. I imagine I can make CFCallback a QObject and provide a signal emitted from operator(). If you have any tips, please share.
I'm afraid it won't work the way you've set it up.
If you want to be able to create the callback in javascript, you need a QObject with an accessible GetVersion(QScriptValue) which the script will the use to pass a script-based implementation of the callback. Note, though, that the callback will not be able to work with untyped (void*) data - you need to pass either a valid QtScript object or QObject with a proper interface (like the Packet one in your example!)
You could then wrap it up like this:
QtScript:
function mycb(packet) {
var pkt_data = packet.getData(); // pkt_data is probably a String or custom object with proper interface so to simplify things get the version as string
var version = pkt_data.toString();
pkt_data.release(); // to simulate delete [] pkt_data; this is part of custom interface
// proceed further with the regex checks
}
GetVersion(mycb); // implies that you define the GetVersion() as a property of the global object
C++:
QScriptValue getVersion(QScriptContext *ctx, QScriptEngine *engine)
{
void *data = ...;
Packet pkt_data = wrapPacketData(data);
// Packet is interface registered with QtScript or inherits QObject
// it has methods getData(), toString() and release()
QScriptValueList args;
QScriptValue pkt_data_param = engine->newQObject(&pkt_data);
args << pkt_data_param;
QScriptValue cb = ctx->argument(0);
Q_ASSERT(cb.isFunction()); // we expect a function object!
cb.call(QScriptValue(), args);
}
QScriptValue getVersionFun = engine->newFunction(getVersion);
engine->globalObject().setProperty(QLatin1String("GetVersion"), getVersionFun);