Class with list initalization of map with function pointers - c++

I'm trying to write a class that will receive a (number of) pair(s) of char arrays (and strings, later down) and function pointers, from which I plan to use the desired function. I'm using a modified version of the code presented in this question for testing purposes, so I have:
#include <iostream>
#include <map>
#include <initializer_list>
using namespace std;
class Test {
std::map<const char*, void(*)()> m_ints;
public:
Test(std::initializer_list<std::pair<const char*, void(*)()>> init):
m_ints(init)
{}
};
void testfunction(){
cout << "This is a test function"<<endl;
}
int main()
{
Test t = { {"hello", &testfunction} };
return 0;
}
Which, when compiled with g++, returns an error:
error: no matching function for call to ‘std::map < const char*, void (\*)()>::map(std::initializer_list < std::pair < const char*, void (*)()> >&)’
m_ints(init)
^
and a VERY long list of notes with candidates. What am I doing wrong, can I initialize my map like this, or should I use an initializer function within the class?

Just like the link you mentioned, you need to const-ify the typename of the key in the initializer-list, e.g.:
std::initializer_list<std::pair<const char* const, void(*)()>>
Notice the key is const char* const and not const char*.
Example
If you don’t want to bother with the painful task of making the type const, you can use the value_type instead, e.g.:
std::initializer_list<decltype(m_ints)::value_type>
Example

Related

How can I send a variable number of pairs of variables to a function

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),...);
}

Using erase-remove idiom for function<void()>

Stacked people.
Iam trying to implement an observer(esque?) pattern for my program. I have a component which stores what functions should be called if an event occours. My prolem is that i dont know how should i erase my function from the container, if the need arises. Tried storing the functions by reference, but iam not sure how to do that(or if thats possible.)
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
enum class EVENT_TYPE{
anEvent
};
class EventableComponent{
map<EVENT_TYPE, vector<function<void()>>> listeners;
public:
void trigger(EVENT_TYPE _et){
for(auto& it : listeners[_et]){
it();
}
}
void registerListener(EVENT_TYPE _et, function<void()> _fn){
listeners[_et].push_back(_fn);
};
void removeListener(EVENT_TYPE _et, function<void()> _fn){
//error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::function<void (void)>'
//(or there is no acceptable conversion)
listeners[_et].erase(remove(listeners[_et].begin(), listeners[_et].end(), _fn), listeners[_et].end());
};
};
int main(){
EventableComponent ec;
// this would become a member function for a class somewhere down the line
auto fn = [](){cout << "Hello.\n"; };
ec.registerListener(EVENT_TYPE::anEvent, fn);
ec.trigger(EVENT_TYPE::anEvent);
ec.removeListener(EVENT_TYPE::anEvent, fn);
ec.trigger(EVENT_TYPE::anEvent);
cin.get();
return 0;
};
Your problem can be reduced to the fact that two std::function instances cannot be compared for equality. std::remove requires operator==, and std::function does not have it. See "Why is std::function not equality comparable?".
Consider the following situation.
Let's say you defined two lambdas in your main:
auto fn = [](){cout << "Hello.\n"; };
auto fn2 = [](){cout << "Hello.\n"; };
Now, are those two equal or not? They do the same thing, but perhaps this is sheer coincidence. Would they become unequal if the second "Hello" became "Hello2"? Would they become unequal if the second one was no longer a lambda but a real function void f()?
The thing is that there can be no generally useful definition of equality for function objects, so it's up to you to define what equality really means in the context of your program.
You have several options to solve the problem at hand. One would be to operate on pointers to std::function objects. Pointers can be compared, and proper use of std::unique_ptr makes sure that deallocation is handled correctly.
Or you assign an identifier to every std::function you use. See the following modified example of your code in which direct storage of std::function<void()> in the vector is replaced with a custom type EventFunction that maps an int to the function object. The example uses std::remove_if to compare only the ints:
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
enum class EVENT_TYPE{
anEvent
};
struct EventFunction {
function<void()> f;
int id;
};
class EventableComponent{
map<EVENT_TYPE, vector<EventFunction>> listeners;
public:
void trigger(EVENT_TYPE _et){
for(auto& it : listeners[_et]){
it.f();
}
}
void registerListener(EVENT_TYPE _et, EventFunction _fn){
listeners[_et].push_back(_fn);
};
void removeListener(EVENT_TYPE _et, int function_id){
//error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::function<void (void)>'
//(or there is no acceptable conversion)
listeners[_et].erase(remove_if(listeners[_et].begin(), listeners[_et].end(),
[&](EventFunction const& e) { return e.id == function_id; }), listeners[_et].end());
};
};
int main(){
EventableComponent ec;
// this would become a member function for a class somewhere down the line
auto fn = [](){cout << "Hello.\n"; };
ec.registerListener(EVENT_TYPE::anEvent, EventFunction{ fn, 1 });
ec.trigger(EVENT_TYPE::anEvent);
ec.removeListener(EVENT_TYPE::anEvent, 1);
ec.trigger(EVENT_TYPE::anEvent);
};
Tried storing the functions by reference, but iam not sure how to do
that(or if thats possible.)
It's not possible because you cannot store references in standard-library containers. But I suppose the idea is similar to the one with pointers I mentioned above.

C++ class and method that search values in map

I have written a minimum class to better submit my problem.
I have three files:
1) test.hpp
#include <cstdlib>
#include <stdio.h>
#include <map>
class test {
public:
test () {}
~test () {}
const char *getPin (const char *);
private:
static const std::map<const char *, const char *> pinIndex;
static std::map<const char *, const char *> initializePins ();
};
2) test.cpp
#include "test.hpp"
const std::map<const char *, const char *> test::pinIndex = test::initializePins ();
std::map<const char *, const char *> test::initializePins () {
std::map<const char *, const char *> pins;
pins.insert (
std::pair<const char *, const char *> (
"AAAA", "BBBB"
)
);
return pins;
}
const char *test::getPin (const char *pinNumber) {
if (pinIndex.count (pinNumber) > 0) {
return pinIndex.at (pinNumber);
}
else {
printf ("Undefined pin %s!\n", pinNumber);
exit (EXIT_FAILURE);
}
}
3) main.cpp
#include "test.hpp"
int main () {
test myExample;
const char *a = myExample.getPin ("AAAA");
exit (EXIT_SUCCESS);
}
When I compile and run it I get this error:
Undefined pin AAAA!
If I remove main.cpp and put the main function in test.cpp file, I do not get me any error and GetPin returns the correct value.
Any idea what I'm doing wrong?
Thank you
Your problem is that you're using char* pointers in your map as key values. To find entries in the map, the implementation uses comparison operations (<) for the given key.
If you're going to compare char* pointers those will almost never be the same, and are completely unrelated to their contents, which is what you're actually want to look for.
A simple solution for your problem would be to change your map type to
std::map<std::string, std::string>
Another possible solution is to provide a class that compares two char* pointers based on content as the 3rd template parameter of the map
std::map<char*, char*, MyComparer>
where MyComparer is s.th. like this
struct MyComparer {
bool operator()( const char*& lhs, const char*& rhs ) const {
return strcmp(lhs,rhs) < 0;
}
};
As an explanation why you experience that seemingly inconsistent behavior, when moving your test to a separate compilation unit:
If you have the same string literals (e.g. "AAAA") appearing in the TU multiple times, the compiler can optimize them to be stored just once, and thus you'll have the same address for all of their appearances.
There is no guarantee that "AAAA" will have the same address as "AAAA" used somewhere else in your program. So finding "AAAA" is not guaranteed to work since the map will be comparing pointer values.
Use a std::map<std::string, std::string> instead of std::map<const char *, const char *>, and your issue should be resolved.
Your solution is quite simple:
#include <iostream>
#include <map>
#include <string>
int
main ()
{
std::map<std::string, std::string> pins { { "AAAA" , "BBBB" }, { "CCCC" , "DDDD" } };
auto lookup = pins.find("AAAA");
if (lookup != pins.end())
std::cout << "Pin AAAA found!" << std::endl;
else
std::cerr << "Pin AAAA not found!" << std::endl;
return 0;
}
A couple of suggestions:
Never use c-strings in C++ code. That is not faster but much more heavy to process. The std::string always knows ones length and performs better in comparisons.
Write as few as you can since KISS (keep it simple stupid) in 90% is the best idea.

Initialise static class member of type vector

I have a class with a private static instance variable of type vector >. I had a strategy for initialising this variable that involved passing a temporary array to a function that was not a class member. This worked on older versions of g++, but not with g++ 4.8.2.
Here's what I'm doing:
initclass.cpp:
#include "initclass.h"
#define NINT 5
vector<pair<int,int> > initInstance(const pair<int,int> *array, int nint)
{
vector<pair<int,int> > vect;
vect.assign(array, array + nint);
return vect;
}
const vector<pair<int,int> > initclass::_intvect =
initInstance((pair<int,int>[NINT]){pair<int,int>(1,2), pair<int,int>(3,4),
pair<int,int>(5,6), pair<int,int>(7,8),
pair<int,int>(9,10)},
NINT);
initclass.h:
#ifndef INITCLASS_H_
#define INITCLASS_H_
#include <utility>
#include <vector>
using namespace std;
class initclass
{
public:
static const vector<pair<int,int> > & intvect() { return _intvect; };
private:
const static vector<pair<int,int> > _intvect;
};
#endif
I can make this work by just declaring the array of pair objects before calling the initInstance function, but I'm wondering if there's a better way (this is of course a simplified example of what I'm trying to do: in my real code, there are many calls to the initialisation function with many arrays, and I'd rather not declare each of them prior to each function call).
EDIT:
I thought briefly I'd found a solution here: Using array init list as temporary in C++11?
The second answer has a conversion from a const array to a (non-const) pointer. In my case this would change the initInstance function call to:
const vector<pair<int,int> > initclass::_intvect = initInstance((pair<int,int>*)(const pair<int,int> []){pair<int,int>(1,2), pair<int,int>(3,4), pair<int,int>(5,6), pair<int,int>(7,8), pair<int,int>(9,10)}, NINT);
This compiles, but the initInstance function never gets called, for some reason.
Yes, there is a better way. You can remove that awful function, which does nothing useful. You can initialize vector like this :
#include <iostream>
#include <vector>
#include <utility>
struct A
{
const static std::vector<std::pair<int,int> > intvect;
};
const std::vector<std::pair<int,int> > A::intvect{ std::pair<int,int>(1,2), std::pair<int,int>(3,4),
std::pair<int,int>(5,6), std::pair<int,int>(7,8),
std::pair<int,int>(9,10) };
int main()
{
(void) A::intvect;
}
This is using the c++11 7th constructor of vector - the one with taking initializer list.

C++ Map of string and member function pointer

Hey so I am making a map with string as the key and a member function pointer as the value. I can't seem to figure out how to add to the map, this doesn't seem to be working.
#include <iostream>
#include <map>
using namespace std;
typedef string(Test::*myFunc)(string);
typedef map<string, myFunc> MyMap;
class Test
{
private:
MyMap myMap;
public:
Test(void);
string TestFunc(string input);
};
#include "Test.h"
Test::Test(void)
{
myMap.insert("test", &TestFunc);
myMap["test"] = &TestFunc;
}
string Test::TestFunc(string input)
{
}
See std::map::insert and std::map for value_type
myMap.insert(std::map<std::string, myFunc>::value_type("test", &Test::TestFunc));
and for operator[]
myMap["test"] = &Test::TestFunc;
You cannot use a pointer to member function without an object. You can use the pointer to member function with an object of type Test
Test t;
myFunc f = myMap["test"];
std::string s = (t.*f)("Hello, world!");
or with a pointer to type Test
Test *p = new Test();
myFunc f = myMap["test"];
std::string s = (p->*f)("Hello, world!");
See also C++ FAQ - Pointers to member functions