C++ Function Map - c++

How will I call the functions?
Lets say I have 3 display functions
void disp1()
{
std::cout<<"\n Disp1 ";
}
void disp2()
{
std::cout<<"\n Disp2 ";
}
void disp3()
{
std::cout<<"\n Disp3 ";
}
I want to create a map of these functions and call them according to the key.
fMap["d1"]
should run that disp1 function. How will I go about this;
If the function has return type other than void I am able to get the value but what about the statements that I need to print. Like the ones above;

You can do this with std::map and std::function:
#include <map>
#include <string>
#include <iostream>
#include <functional>
void disp1()
{
std::cout<<"\n Disp1 ";
}
void disp2()
{
std::cout<<"\n Disp2 ";
}
void disp3()
{
std::cout<<"\n Disp3 ";
}
int main()
{
// add the functions as an initializer-list
std::map<std::string, std::function<void()>> fMap
{
{"d1", disp1}
, {"d2", disp2}
, {"d3", disp3}
};
// or add them one at a time
fMap["d1"] = disp1;
fMap["d2"] = disp2;
fMap["d3"] = disp3;
fMap["d1"](); // call them using ()
fMap["d2"]();
fMap["d3"]();
}

To store the lookup index, you would use std::map. This comprises key-value pairs (mymap[ key ] = value). C++ allows you to store a pointer to a function, here I use the using keyword to create a type name, func_t, of the type of fingerprint our function will have: void (*)(void) (this is how you say "pointer to a function which takes void and returns void).
#include <iostream>
#include <string>
#include <map>
// the functions we intend to map
void disp1()
{
std::cout<<"Disp1\n";
}
void disp2()
{
std::cout<<"Disp2\n";
}
void disp3()
{
std::cout<<"Disp3\n";
}
int main() {
// create a new type, func_t, which describes a pointer
// to a void function that takes no parameters (void).
using func_t = void (*)(void);
// declare a map from a string key to a func_t value,
// and initialize it with a mapping of f1->disp1, f2->disp2
// and f3->disp3
std::map<std::string, func_t> functionMap = {
{ "f1", disp1 }, { "f2", disp2 }, { "f3", disp3 }
};
// declare a string for reading input
std::string input;
// loop until there is no more input on std::cin.
while (std::cin.good()) {
// prompt
std::cout << "Which disp (f1, f2, f3)? ";
// fetch the next line of text from cin, without the \n
std::getline(std::cin, input);
// if the input is empty we ran out of input or the user
// input a blank line. either way, stop.
if (input.empty())
break;
std::cout << "You chose " << input << "\n";
// look for the key in the map. if the key is not found,
// it will equal the special iterator functionMap.end()
auto it = functionMap.find(input);
// If it's not functionMap.end then we have a valid iterator.
// it->first is the key, it->second is the function pointer.
if (it != functionMap.end()) {
// to use a function pointer, just add the () with any
// arguments after the variable name.
// remember, it->second is the function pointer.
it->second();
} else {
std::cout << "Invalid entry.\n";
}
}
}
Live demo: http://ideone.com/4Xlow1

The answer to your first question is to use std::function in your map.
std::map<std::string, std::function<void>> fMap {
{"d1", disp1},
{"d2", disp2},
{"d3", disp3}
};
Your second question about having a return value I would need to think about.

Solved with the help of #Galik's comment
#include <iostream>
#include <functional>
#include <algorithm>
#include <map>
typedef std::function<void()> func_t;
typedef std::map<std::string, func_t> func_t_map;
void disp1()
{
std::cout<<"\n Display 1 "<<std::endl;
return;
}
void disp2()
{
std::cout<<"\n Display 2 "<<std::endl;
return;
}
void disp3()
{
std::cout<<"\n Display 3 "<<std::endl;
return;
}
void disp4()
{
std::cout<<"\n Display 4 "<<std::endl;
return;
}
int main()
{
func_t_map fMap;
fMap["d1"] = disp1;
fMap["d2"] = disp2;
fMap["d3"] = disp3;
fMap["d4"] = disp4;
fMap["d2"]();
return 0;
}

Related

How to avoid multiple if-else statement while reading a file?

I am trying to change multiple if-else statement in my program with either switch and enum or some other object-oriented approach. My purpose is in my code there should not be if-else or very less.
class myClass
{
void readFile();
}
void myClass :: readFile()
{
std::string lineByLine;
std::ifstream myfile;
myfile.open(file path);
if (myfile.is_open())
{
while (std::getline(myfile, lineByLine))
{
std::pair<std::string, std::string> p1 = FindFirstWord(lineByLine);
// FindFirstWord --> will break the line into 2 words, First word and remaining words
while (p1.first.compare("}")) {
if (!p1.first.compare("SCALE")) {
// calling some function
} else {
if (!p1.first.compare("symbol")) {
// calling some function
} else {
if (!p1.first.compare("set_minimum_boundary")) {
// calling some function
} else {
if (!p1.first.compare("line")) {
// calling some function
} else {
if (!p1.first.compare("circle")) {
// calling some function
} else {
if (!p1.first.compare("arc")) {
// calling some function
} else {
if (!p1.first.compare("pin")) {
// calling some function
}
}
}
}
}
}
}
I want to remove the chain of if-else statement using either by switch and enum or some other object oriented approach.
Use a mapping from keyword to handling functions, this is a quick draft of that idea. (You might want to lookup lambda functions)
#include <functional>
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
std::istringstream filestream
{
"SCALE\n"
"symbol\n"
};
class myClass
{
public:
myClass() :
m_handlers
{
{"SCALE", [=]() { HandleScale(); }},
{"symbol", [=]() { HandleSymbol(); }}
}
{
};
void readFile(std::istream& myfile)
{
std::string keyword;
while (myfile >> keyword)
{
// skipped line parsing, to show minimal sample
// lookup if there is a handler for read keyword
auto it = m_handlers.find(keyword);
if ( it != m_handlers.end())
{
auto& callback = it->second;
callback();
}
}
}
void HandleScale()
{
std::cout << "handling scale\n";
}
void HandleSymbol()
{
std::cout << "handling symbol\n";
}
private:
std::unordered_map<std::string, std::function<void()>> m_handlers;
};
int main()
{
myClass object;
object.readFile(filestream);
return 0;
}
If you want to use an enum with a switch, you can do it in the following way:
Define an enum for your action types:
enum class ActionType
{
Scale,
Symbol,
SetMinimumBoundary,
//...
INVALID
};
Add a function for converting a string to the action type enum:
#include <string>
ActionType StringToActionType(std::string const & actionTypeStr)
{
if (actionTypeStr == "SCALE") return ActionType::Scale;
if (actionTypeStr == "symbol") return ActionType::Symbol;
if (actionTypeStr == "set_minimum_boundary") return ActionType::SetMinimumBoundary;
// ...
return ActionType::INVALID;
}
Then use it as shown below (you can add similar logic to myClass::readFile) :
int main()
{
std::string someActionTypeStr; // Initialized to your p1.first
ActionType actionType = StringToActionType(someActionTypeStr);
switch (actionType)
{
case ActionType::Scale: // calling some function
break;
case ActionType::Symbol: // calling some function
break;
case ActionType::SetMinimumBoundary: // calling some function
break;
// ...
default:
// report error ...
break;
}
return 0;
}

Changing a value of an element in an object that is stored in a vector in another object through an external function in C++

So made a class called ‘Item’, and the object of that class will have a 100% condition at the start, the Player stores items (with name “apple” in this case) whenever I tell him to. In the degradeT function I want to pass the whole vector containing the items that the player has picked up by far and then change the condition of each Item in that vector by -1 through the chCond function.
first error:
initial value of reference to non-const must be an lvalue
second error:
'void degradeT(std::vector<Item,std::allocator<_Ty>> &)': cannot convert argument 1 from 'std::vector<Item,std::allocator<_Ty>>' to 'std::vector<Item,std::allocator<_Ty>> &'
#include "pch.h"
#include <iostream>
#include <string>
#include <vector>
using std::cout; using std::cin; using std::endl;
using std::string; using std::vector; using std::to_string;
class Item {
private:
string name; // Item name
float condition; // Item condition
bool consumable; // Is the item consumable
public:
Item() {}
Item(string a, float b, bool c) { name = a; condition = b; consumable = c; }
Item(string a, bool c) { name = a; condition = 100.f; consumable = c; }
string getName() {
return name;
}
float getCond() {
return condition;
}
bool isCons() {
return consumable;
}
void chCond(float a) { // Change Item condition
condition += a;
}
};
//-----------------------
class Player {
private:
vector<Item> plItems; // Item container
public:
Player() {}
void pickUpItem(Item a) { // Adding Items to inventory
plItems.push_back(a);
cout << a.getName() << " added to inventory!\n";
}
void checkItemConds() { // Checking condition of all items
for (unsigned int a = 0, siz = plItems.size(); a < siz; a++) {
cout << plItems[a].getName() << "'s condition is: " << plItems[a].getCond() << "%\n";
}
}
Item returnItem(unsigned int a) { // Return a specific Item
return plItems[a];
}
int getCurInvOcc() { // Get cuurent inventory occupation
return plItems.size();
}
vector<Item> getPlItems() { // Return the vector (Item container)
return plItems;
}
};
//-------------------------
void degradeT(vector<Item>& Itemss); // Degrade item after some time
//-------------------------
int main()
{
Player me; // me
string inp; // input
int num = 1; // apple 1, apple 2, apple 3...
while (inp != "exit") {
cin >> inp;
if (inp == "addApple") {
Item apple(("apple " + to_string(num)), true);
me.pickUpItem(apple);
num++;
}
if (inp == "checkItemConds") {
me.checkItemConds();
}
if (inp == "timeTick") {
// This doesn't have anything to do with time I just want to test the function manually
degradeT(me.getPlItems());
}
}
system("PAUSE");
return 0;
}
void degradeT(vector<Item> &Itemss) {
for (unsigned int a = 0, siz = Itemss.size(); a < siz; a++) {
Itemss[a].chCond(-1);
cout << Itemss[a].getName() << endl;
}
}
I'm not sure what your question is, but your error is related to the function void degradeT(vector<Item> & Itemss).
This functions expects a reference but you are passing an r-value. You can either return a reference with getPlItems() or pass an l-value to degradeT.

Passing function as an argument to different function in different file

So, I want to pass function, that is in engine.cpp file, as an argument, and that's what I did:
typedef RFun(*wsk)(double, int);
RFun Engine::f_line(double *&values, int howmany)
{
RFun line;
for(int i = 0; i < howmany; i++)
{
line.result_values[i] = (2 * values[i]) + 6;
}
return line;
}
RFun counter(double *&values, int howmany, wsk function)
{
return function(*values, howmany);
}
and now I want to call the counter function in other .cpp file and pass f_line function inside as parameter. How can I achieve that?
Here is a simple example how to use std::function.
#include <iostream>
#include <functional>
using namespace std;
void func1()
{
// a function that takes no parameters and does nothing
cout << "in global func1" << endl;
}
class Example
{
public:
int value;
void memberfunc()
{
cout << "in memberfunc. value=" << value << endl;
}
};
void CallAFunction( std::function< void() > functocall )
{
functocall(); // call it normally
}
int main()
{
// call a global function
CallAFunction( func1 ); // prints "in global func1"
// call a member function (a little more complicated):
Example e;
e.value = 10;
CallAFunction( std::bind( &Example::memberfunc, std::ref(e) ) );
// prints "in memberfunc. value=10"
}
Try it out here
Success time: 0 memory: 15240 signal:0
in global func1
in memberfunc. value=10

getting error on vector<unique_ptr<X>> v

I´m quiet new on C++ and currently learning to understand smart pointers.Therefore I`m currently working on a little console-program for inserting,searching and deleting songs ... for learning purposes to get used to the stuff =)
Here is my code:
Song.hpp
#pragma once
#include <vector>
#include <memory>
#include <string>
class Song
{
public:
typedef std::unique_ptr<Song> pSong;
public:
Song();
~Song();
void setTitle(std::string title);
void setArtist(std::string artist);
void checkSong(std::string item, int iterator);
void get();
private:
std::string _title;
std::string _artist;
};
Song.cpp
#include "Song.hpp"
#include <iostream>
Song::Song()
{
}
Song::~Song()
{
}
void Song::setTitle(std::string title)
{
_title = title;
}
void Song::setArtist(std::string artist)
{
_artist = artist;
}
void Song::checkSong(std::string item, int iterator)
{
if (_artist == item || _title == item)
{
std::cout << "Item found on Slot: " << iterator << std::endl;
}
else
{
std::cout << "No item found!" << std::endl;
}
}
void Song::get()
{
std::cout << _artist << " - " << _title << std::endl;
}
Main.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>
#include "Song.hpp"
//prototype
void IntVector();
void SongVector();
Song* setSong(std::string title, std::string artist);
void find(std::string item, std::vector<Song::pSong> v);
std::vector<Song::pSong> SongList;
int main()
{
int k;
SongVector();
std::cin >> k;
return 0;
}
void IntVector()
{
// Create Vector
std::vector<std::unique_ptr<int>> v;
// Create a few unique_ptr<int> instances and fill them with ints
v.push_back(std::unique_ptr<int>(new int(30)));
v.push_back(std::unique_ptr<int>(new int(600)));
v.push_back(std::unique_ptr<int>(new int(200)));
v.push_back(std::unique_ptr<int>(new int(20)));
v.push_back(std::unique_ptr<int>(new int(200)));
v.push_back(std::unique_ptr<int>(new int(160)));
v.push_back(std::unique_ptr<int>(new int(4)));
v.push_back(std::unique_ptr<int>(new int(5)));
v.push_back(std::unique_ptr<int>(new int(315)));
// define vector<int> for storing values of the unique_ptr
std::vector<int> intList;
for (int i = 0; i < v.size(); i++)
{
// get memory-adress of each element
auto result = v[i].get();
// store value of result-pointer in Vector
intList.push_back(*result);
std::cout << *result << std::endl;
}
// Sort int of new Vector
std::sort(intList.begin(), intList.end());
// Loop through intList and cout
for (int i = 0; i < intList.size(); i++)
{
std::cout << intList[i] << std::endl;
}
}
void SongVector()
{
Song* first = setSong("Afroki","Steve Aoki");
Song* secound = setSong("Hype", "Steve Aoki");
Song* third = setSong("Madness", "Steve Aoki");
Song* fourth = setSong("Cake Face", "Steve Aoki");
SongList.push_back(Song::pSong(first));
SongList.push_back(Song::pSong(secound));
SongList.push_back(Song::pSong(third));
SongList.push_back(Song::pSong(fourth));
for (const auto& song : SongList)
{
song->get();
}
find("Madness", SongList);
}
Song* setSong(std::string title, std::string artist)
{
Song* song = nullptr;
song = new Song;
song->setArtist(artist);
song->setTitle(title);
return song;
}
void find(std::string item, std::vector<Song::pSong> v)
{
int i = 0;
for (const auto& song : v)
{
song->checkSong(item,i);
i++;
}
}
I got following error:
std::unique_ptr<Song,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function
1> with
1> [
1> _Ty=Song
1> ]
I found out, that this error only occurs, when calling my find(...)-method, so I´m guessing that somewhere in there is my mistake, but I just cant find out, what Ive done wrong. Would appreciate your help.
std::unique_ptr provide unique ownership (hense the name) which means beside other you cannot copy instance of std::unique_ptr - which would mean shared ownership. When you passing std::vector<std::unique_ptr<whatever>> by value you creating a copy of vector instance, which tries to copy each element. So simplest solution would be to pass std::vector instance by const reference (as you do not have intention to modify vector):
void find( const std::string &item, const std::vector<Song::pSong>& v);
beside fixing your problem passing by (const) reference is more efficient for non trivial objects, so you can also use it for std::string
in your intVector() function:
for (int i = 0; i < v.size(); i++)
{
// get memory-adress of each element
auto result = v[i].get();
// store value of result-pointer in Vector
intList.push_back(*result);
std::cout << *result << std::endl;
}
you do not really need to get raw pointer, just use std::unique_ptr itself:
for (int i = 0; i < v.size(); i++)
{
// get smart pointer for each element
const auto &result = v[i];
// store value of result-pointer in Vector
intList.push_back(*result);
std::cout << *result << std::endl;
}
void find(std::string item, std::vector<Song::pSong> v)
You need to pass vectors by reference. Add &.
void find(std::string item, std::vector<Song::pSong>& v)
Don't forget to also change the prototype of the function.

c++ reference to function pointer dynamically

I have one application in which following task are to be done
1.) UI application will send command code (integer value).
2.) DLL interface(in c++) will get that integer value and execute corresponding command function.
commands name and command code are maintained as
#define PING 50
there will be 500 commands and applying SWITCH CASE will not sound good. so i decided to implement function pointer in my code as below
#include "stdafx.h"
#include<iostream>
#define PING 20
using namespace std;
//extern const int PING = 10;
void ping()
{
cout<<"ping command executed";
}
void get_status(void)
{
cout<<"Get_status called"<<endl;
}
class ToDoCommands
{
public:
void getCommand( void (*CommandToCall)() );
};
void ToDoCommands::getCommand( void (*CommandToCall)())
{
void (*CommandToCall1)();
CommandToCall1 = CommandToCall;
CommandToCall1();
}
int main()
{
int code;
ToDoCommands obj;
cout<<"enter command code";
cin>>code; // if UI send 50 then Ping function get executed as #define PING 50
obj.getCommand(ping); // here m passing ping manually..
//obj.getCommand(get_status);
return 0;
}
how can i pass command name corresponding to command code in
obj.getCommand(ping);
You are almost there: make a std::map of std::string to function pointer, initialize it with data pairing a string name to a corresponding function pointer, and then use that map at runtime to pick the correct pointer based on the string parameter passed in.
#include <iostream>
#include <string>
#include <map>
using namespace std;
void ping() {
cout << "ping" << endl;
}
void test() {
cout << "test" << endl;
}
int main() {
map<string,void(*)()> m;
m["ping"] = ping;
m["test"] = test;
// I am using hard-coded constants below.
// In your case, strings will come from command line args
m["test"]();
m["ping"]();
return 0;
}
Link to a demo with std::map.
Here is how you can do it without a map (it will be slower because of the linear search, but you can fix it by ordering names alphabetically and using binary search).
#include <iostream>
#include <cstring>
using namespace std;
void ping() {
cout << "ping" << endl;
}
void test() {
cout << "test" << endl;
}
typedef void (*fptr_t)();
int main() {
const fptr_t fptrs[] = {test, ping};
const char *names[] = {"test", "ping"};
const char *fname = "test";
for (int i = 0 ; i != 2 ; i++) {
if (!strcmp(fname, names[i])) {
fptrs[i]();
break;
}
}
return 0;
}
Link to a demo with arrays.
Declare an array of function pointers. Where you treat the index as your "code". For example:
void foo(){
printf("foo\n");
}
void bar(){
printf("bar\n");
}
int main(void)
{
void (*code_to_function[100])();
int code;
code_to_function[0] = foo;
code_to_function[1] = bar;
printf("Enter code: ");
scanf("%d", &code);
code_to_function[code]();
return 0;
}
Please note that for this rudimentary example, inputting integer code other than 0 and 1 will result in a segfault.
I should say #dasblinkenlight is right but if you don't want to use std::map you should implement a map yourself. This can be buggy and not a optimized way, but if you don't want to use STL, it seems you should implement it yourself.
You can use 2 arrays with corresponding indices. One of them is a char * array and another one is function pointers. They are better to be encapsulated in a class named something like MyMap.
class MyMap {
public:
...
inline void add(char *name, (void (*ptr)(void)) ) {
names_[currIndex_] = name; // Or stcpy
ptrs_[currIndex_] = ptr;
currIndex_++;
}
inline (void(*)(void)) get(char *name) {
int foundIndex = -1;
for (int i = 0; i < currIndex_; i++) {
// Find matching index
}
if (foundIndex_ >= 0) {
return ptrs_[foundIndex_];
}
return NULL;
}
private:
int currIndex_;
char *names_[10];
(void (*ptrs_[10])(void));
};