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;
}
Related
At first it seemed clear that I shouldn't be able to do this, but then I discovered that it can be done with free functions.
Here is an example where I pass void() functions from Child to Parent. The parent calls the function when their Frame comes up.
I have figured out the syntax to pass free functions with arguments, but I can't figure out how to pass a member function of Child with an argument.
Please help.
#include <iostream>
#include <vector>
#include <map>
#include <functional>
void f_Free1() { std::cout << "Hi there hello. I'm a free function without arguments."; }
void f_Free2(int i) { std::cout << "Hi there hello. I'm a free function with an argument. It's " << i; }
class Parent
{
std::map<unsigned int, std::vector<std::function<void()>>> Tasks;
protected:
void addTask(unsigned int frame, std::function<void()> task) { Tasks[frame].push_back(task); }
public:
virtual ~Parent() {}
unsigned int Frame = 0;
void tick()
{
if (Tasks.count(Frame))
{
for (auto task : Tasks[Frame])
{
task();
}
}
Frame++;
}
};
class Child : public Parent
{
void f_Child1() { std::cout << "This is a private Child function without arguments. "; }
void f_Child2(int i) { std::cout << "This is a private Child function with an argument. It's " << i; }
public:
Child()
{
addTask(3, f_Free1);
addTask(5, [a = int(4)] () { f_Free2(a); } ); // THIS WORKS!!!
addTask(7, std::function<void()> { std::bind(&Child::f_Child1, this) });
addTask(9, std::function<void()> { std::bind([a = int(4)]() { &Child::f_Child2(a), this) } }); // CAN'T MAKE THIS WORK
}
};
int main()
{
Child child;
for (unsigned int i = 0; i < 12; i++)
{
std::cout << "[" << child.Frame << "]";
child.tick(); // runs tasks whose frames are up
std::cout << std::endl;
}
return 0;
}
Ditch the bind.
Use [this]() { f_Child1(); } and [this]() { f_Child(4); }.
Also, the free version can be just []() { f_Free2(4); }.
std::bind's syntax would be:
std::bind(&f_Free1)
std::bind(&f_Free2, 4)
std::bind(&Child::f_Child1, this)
std::bind(&Child::f_Child2, this, 4)
But lambda is simpler for most people:
&f_Free1 is fine, else [](){ return f_Free1(); }
[](){ return f_Free2(4); }
[this]() { return this->f_Child1(); )
[this]() { return this->f_Child2(4); )
return can be omitted here as functions return void.
this-> can be omitted in lambda.
you might capture more or differently for arguments.
I'm using the following example from Stroustrup C++ 4th Ed. Page 218. My question is regarding the destructor.
Questions:
How does placement new(&s) string{ss} allocate room for the new string when it's my understanding that union size is fixed to the largest member? Is string s not a 0 element string? If so, how does the placement new create a larger string if there is not enough space in the union?
#include <iostream>
#include <string>
using namespace std;
class Entry2 { // two alternative representations represented as a union
private:
enum class Tag { number, text };
Tag type; // discriminant
union { // representation
int i;
string s; // string has default constructor, copy operations, and destructor
};
public:
struct Bad_entry { }; // used for exceptions
string name;
Entry2(int n) : type{Tag::number}, i{n} { };
Entry2(string ss) : type{Tag::number} { new(&s) string{ss}; };
~Entry2();
Entry2& operator=(const Entry2&); Entry2(const Entry2&);
// ...
int number() const; string text() const;
void set_number(int n);
void set_text(const string&); // ...
};
Entry2::~Entry2()
{
if (type==Tag::text)
s.~string();
}
int Entry2::number() const
{
if (type!=Tag::number) throw Bad_entry{};
return i;
}
string Entry2::text() const
{
if (type!=Tag::text) throw Bad_entry{};
return s;
}
void Entry2::set_number(int n)
{
if (type==Tag::text) {
s.~string();
type = Tag::number;
}
i = n;
}
void Entry2::set_text(const string& ss)
{
if (type==Tag::text)
s = ss;
else {
new(&s) string{ss};
type = Tag::text;
}
}
Entry2& Entry2::operator=(const Entry2& e)
{
if (type==Tag::text && e.type==Tag::text) {
s = e.s; // usual string assignment
return *this;
}
if (type==Tag::text)
s.~string(); // explicit destroy (§11.2.4)
switch (e.type) {
case Tag::number:
i = e.i;
break;
case Tag::text:
new(&s) string{e.s};
type = e.type;
}
return *this;
}
int main(int argc, char *argv[])
{
Entry2 e0(0);
cout << e0.number() << endl;
try {
e0.text();
} catch (...) {
cout << "caught except" << endl;
}
e0.set_text("abcd");
cout << e0.text() << endl;
return 0;
}
No, the destructor should not always do this. Remember that in a union, only one of the fields is actually active at any one time. If the std::string member of the union isn't active, then calling its destructor would be a Bad Thing (cause undefined behavior) because there wasn't a string there to destroy. Instead, we instead only call the destructor on that std::string member if at some previous point in time we activated the std::string there.
Hope this helps!
i have a big problem.. I wonna select the Storage Service via a wrapper class. The returning value must be an object within the storage service class. I pasted my current approach. But my mindset didn't worked so far.
Error:
error: inconsistent deduction for auto return type: ‘SQL*’ and then ‘REDIS*’ return new REDIS();
The big wish is to have an interface class which defines the struct and some "driver classes" which contains all necessary operations for the target storage service.
I hope you have another approach, how I can solve this problem..
#include <iostream>
class StorageTemplate {
public:
virtual bool UserhasSurName() = 0;
virtual bool UserhasGivenName() = 0;
};
class SQL: public StorageTemplate {
public:
bool UserhasSurName() {
//A SQL QUERY
return true;
}
bool UserhasGivenName() {
//AN ANOTHER SQL QUERY
return true;
}
};
class REDIS: public StorageTemplate {
public:
bool UserhasSurName() {
//A REDIS CALL
return false;
}
bool UserhasGivenName() {
//A REDIS CALL
return false;
}
};
class controller {
public:
auto test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
}
};
int main(int argc, char const *argv[])
{
controller cont;
auto schnitzel = cont.test(1);
auto mitzel = cont.test(2);
std::cout << schnitzel->UserhasSurName() << std::endl;
std::cout << mitzel->UserhasSurName() << std::endl;
}
The problem you are facing is the following: Consider your function
auto test(int select) {
if (select == 1) {
return new SQL();
} else {
return new REDIS();
}
}
If you trying to evaluate test(1) this expands to
auto test(int select) {
if (true) {
return new SQL();
} else {
return new REDIS();
}
}
which results in a type error!
I show you three workarounds for your problem:
1. Function template and if constexpr
Make test a function template and check for the correct type using the C++17 feature if constexpr:
template<typename T>
auto test() {
if constexpr(std::is_same<T, SQL>::value) {
return new SQL();
} else {
return new REDIS();
}
}
Use it in main() like this:
int main(){
controller cont;
auto schnitzel = cont.test<SQL>();
auto mitzel = cont.test<REDIS>();
std::cout << schnitzel->UserhasSurName() << std::endl;
std::cout << mitzel->UserhasSurName() << std::endl;
}
2. Function template and std::unique_ptr
If you want to avoid using the if constexpr you can simply return an instance of std::unique_ptr instead of a raw pointer. This is the preferred way to do:
template<typename T>
auto test() {
return std::unique_ptr<T>(new T);
}
Alternatively you can just return std::make_unique<T>().
3. Returning an instance of the base class
This is is most obvious solution to avoid the type error: Just return an instance of the base class. As above a solution using smart pointers is preferred here:
std::unique_ptr<StorageTemplate> test(const int select) {
if (select == 1) {
return std::make_unique<SQL>();
} else {
return std::make_unique<REDIS>();
}
}
If you really want to avoid using smart pointers just use raw ones like this:
StorageTemplate* test(const int select) {
if (select == 1) {
return new SQL();
} else {
return new REDIS();
}
}
in this code
auto test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
auto can't be deduced because it only match to exact type. so even if SQL and REDIS inherite from StorageTemplate, StorageTemplate won't be deduced. you need to spécifie the type
StorageTemplate* test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
Error return Auto in test(),it's return two different types. Change by StorageTemplate*
class controller {
public:
StorageTemplate* test(int select) {
if( select == 1)
{
return new SQL();
} else {
return new REDIS();
}
}
};
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;
}
I have a small problem with inheritance in my code - it suddenly stops working when I add identical code in another "if" statement in my loop.
Here is the code I use for "main.cpp":
#include "bibl.h"
using namespace std;
int main()
{
int o;
Vec2 test;
while(1)
{
cout<<"1. Add item and show."<<endl<<"2. Show."<<endl;
cin>>o;
if(o==1)
{
InhItem a("Test",100);
test.addItem(&a);
cout<<"This show works:"<<endl;
test.getVec1(0)->getItem(0)->Show();//This code works.
}
else if(o==2)
{
cout<<"This show doesn't work:"<<endl;
test.getVec1(0)->getItem(0)->Show();//This doesn't.
}
}
system("pause");
return 0;
}
And the code for "bibl.h":
#ifndef TEST1
#define TEST1
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Item
{
protected:
string im;
string theme;
public:
Item(string im=" ", string theme=" "):im(im),theme(theme)
{
}
~Item()
{
}
string getTheme()
{
return theme;
}
virtual void Show()
{
}
};
class InhItem:public Item
{
int tst;
public:
InhItem(string im=" ", int tst=0):Item(im),tst(tst)
{
}
~InhItem()
{
}
void Show()
{
cout<<tst<<endl;
}
};
class Vec1
{
vector<Item*> Vec1a;
string theme;
public:
Vec1(string theme=" "):theme(theme)
{
}
~Vec1()
{
}
void addToVec1a(Item *item)
{
Vec1a.push_back(item);
}
string getTheme()
{
return theme;
}
Item *getItem(int p)
{
return Vec1a[p];
}
};
class Vec2
{
vector<Vec1*> Vec2a;
public:
Vec2()
{
}
~Vec2()
{
}
void addToVec2(Vec1 *vec1)
{
Vec2a.push_back(vec1);
}
void addItem(Item *item)
{
for(int i=0;i<=Vec2a.size();i++)
{
if(i==Vec2a.size())
{
addToVec2(new Vec1(item->getTheme()));
Vec2a[i]->addToVec1a(item);
break;
}
else if(Vec2a[i]->getTheme().compare(item->getTheme())==0)
{
Vec2a[i]->addToVec1a(item);
break;
}
}
}
Vec1 *getVec1(int r)
{
return Vec2a[r];
}
};
#endif
When I try to use the 2nd "if" after adding the item with 1st, it doesn't show - in 1st "if" test.getVec1(0)->getItem(0)->Show(); works, but in another it doesn't.
What is the cause of this problem and how can I fix it?