How to register mutli LLVM Pass in one file? - c++

I register the function Pass using registerPipelineParsingCallback following the new Pass Manager requirements.
However, I found that I can only register single functionPass or ModulePass. How can I register both of them in one file?
my register code is
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
llvmGetPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef PassName, FunctionPassManager &FPM,...) {
if (PassName == "fiber") {
FPM.addPass(HelloNewPMPass());
return true;
}
return false;
}
);
}
};
}
I have both function Pass and Module pass in one file like below:
namespace {
struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
PreservedAnalyses pa = PreservedAnalyses::all();
return pa;
}
};
struct SecondPass: public PassInfoMixin<StringEncryptionPass> {
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
errs() << M.getName() << "\n";
PreservedAnalyses pa = PreservedAnalyses::all();
return pa;
}
};
}
Diving into the interface of registerPipelineParsingCallback, I found that the lamda function's parameter which registerPipelineParsingCallback need could be used either FunctionPassManager or ModulePassManager.
I wonder how can I register Module&Function PassManager at the same time.
Using the lambda function like below doesn't work
PB.registerPipelineParsingCallback(
[](StringRef PassName, FunctionPassManager &FPM, ModulePassManager &MPM) {
...
return false;
}
);

i think you can use PB.registerPipelineParsingCallback twice.
just like:
return {LLVM_PLUGIN_API_VERSION, "HelloNewPMPass", "v0.1",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback([](StringRef PassName, FunctionPassManager &FPM,...){
if(PassName = "hellonewpmpass"){FPM.addPass(HelloNewPMPass());return true;}
return false;
});
PB.registerPipelineParsingCallback([](StringRef PassName, ModulePassManager &MPM,...){
if(PassName = "secondpass"){MPM.addPass(SecondPass());return true;}
return false;
});
}
};
the first registerPipelineParsingCallback receive FunctionPassManager, the second receive ModulePassManager

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;
}

flooding the views with update requests issue in observer pattern(C++)

I have some MVC code, it uses the observer mode, such as below:
void Model::ChangeMethod1()
{
m_A = m_A + 1;
...
Notify();
}
void Model::ChangeMethod2()
{
m_A = m_A + 2;
...
Notify();
}
void Model::ChangeMethod3()
{
ChangeMethod1();
ChangeMethod2();
Notify();
}
void Model::ChangeMethod4()
{
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
Notify();
}
There are many functions like ChangeMethodX which will make changes to the Model, and notify the viewers, and when the viewers receive the events, they will refresh/update themselves.
You see, each function ChangeMethodX has a Notify() function, which internally send an event to the observer.
But I don't want the observer receive too many events in the each function, because there will too many events, I would like each top level function call whether it has any internal function calls only send one update event to the viewer.
I think this is a very common issue that happens in many situations, such as the MVC mode, as a model will notify viewers to get updated. But we have to avoid the flooded events if the model changes several times inside a top level function call.
I thought of 2 possible approaches:
If the subject is completely under your control and this solution is not too invasive, you could add an optional parameter specifying whether the called ChangeMethodX is a top level function, like this:
void Model::ChangeMethod1(bool topLevel = true)
{
m_A = m_A + 1;
...
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod2(bool topLevel = true)
{
m_A = m_A + 2;
...
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod3(bool topLevel = true)
{
ChangeMethod1(false);
ChangeMethod2(false);
NotifyIfTopLevel(topLevel);
}
void Model::ChangeMethod4(bool topLevel = true)
{
ChangeMethod1(false);
ChangeMethod2(false);
ChangeMethod3(false);
NotifyIfTopLevel(topLevel);
}
void Model::NotifyIfTopLevel(bool topLevel)
{
if (topLevel)
Notify();
}
However, it is ugly most of the time and it could dirty your interface.
The second approach you could choose is on the other hand risky if you have to deal with concurrency. Moreover if you catch an exception and you handle it, you must remember to bring the object back in a correct state (is_changing-- if not called yet), otherwise the observers are not going to receive notifications anymore.
int is_changing = 0;
void Model::ChangeMethod1()
{
m_A = m_A + 1;
...
NotifyIfNotChanging();
}
void Model::ChangeMethod2()
{
m_A = m_A + 2;
...
NotifyIfNotChanging();
}
void Model::ChangeMethod3()
{
is_changing++;
ChangeMethod1();
ChangeMethod2();
is_changing--;
NotifyIfNotChanging();
}
void Model::ChangeMethod4()
{
is_changing++;
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
is_changing--;
NotifyIfNotChanging();
}
void Model::NotifyIfNotChanging()
{
if (is_changing == 0)
Notify();
}
If you have that many ChangeMethodX methods, maybe consider using an Aspect Oriented framework to separate the concern of notifying observers. Especially if you need to repeat is_changing++/-- or trivially the Notify call, moving them in the appropriate aspect class would definitely be more readable.
EDIT
As for the RAII approach, it is in my opinion kind of overused here since you do not have resources to release, creating and disposing objects every time is quite overkilling for your needs.
By the way, if you want to follow this path, then I recommend you to fix some code smells.
You are not encapsulating appropriately the SetTopLevelCall. It is not supposed to be public because the user of your class must not mess with it.
There is a new public class DeferredEventSender that is tightly coupled to your Model class. The worst part is that it is responsible for the Notify method, that should be called by the Model itself. Moreover, you are ruling out the possibility to need to access the Model private fields and functions.
Here is how I would face these issues, even though it is not perfect yet.
class Model
{
public:
Model()
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
protected:
class DeferredEventSender
{
public:
DeferredEventSender(Model* m)
{
_m = m;
doCallNotify = _m->topLevel;
_m->topLevel = false;
}
~DeferredEventSender()
{
if (doCallNotify)
{
_m->Notify();
_m->topLevel = true;
}
}
Model* _m;
bool doCallNotify;
};
bool topLevel = true;
int m_A;
int m_B;
};
void Model::ChangeMethod1()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 1;
}
...
I just follow Marco Luzzara's second approach, and create a simple demo C++ code, see below:
Revision 1:
#include <iostream>
using namespace std;
class Model
{
public:
Model()
: m_TopLevelCallScope(false)
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
bool IsTopLevelCall()
{
return m_TopLevelCallScope;
}
void SetTopLevelCall(bool topLevel)
{
m_TopLevelCallScope = topLevel;
}
private:
// if this variable is true, it means a top level call scope is entered
// then all the inner call should not send event, the final event could
// send when the top level sender get destructed
bool m_TopLevelCallScope;
// other members
int m_A;
int m_B;
};
// this is a deferred notification
// each function should create a local object
// but only the top level object can finally send a notification
class DeferredEventSender
{
public:
DeferredEventSender(Model* model)
: m_Model(model)
{
if(m_Model->IsTopLevelCall() == false)
{
m_Model->SetTopLevelCall(true);
m_TopLevelCallScope = true;
}
else
{
m_TopLevelCallScope = false;
}
}
~DeferredEventSender()
{
if (m_TopLevelCallScope == true)
{
// we are exiting the top level call, so restore it to false
// it's time to send the notification now
m_Model->SetTopLevelCall(false);
m_Model->Notify();
}
// do nothing if m_TopLevelCallScope == false
// because this means we are in a inner function call
}
bool m_TopLevelCallScope;
Model* m_Model;
};
void Model::ChangeMethod1()
{
DeferredEventSender sender(this);
m_A = m_A + 1;
}
void Model::ChangeMethod2()
{
DeferredEventSender sender(this);
m_A = m_A + 2;
}
void Model::ChangeMethod3()
{
DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
}
void Model::ChangeMethod4()
{
DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
}
void Model::Notify()
{
cout << "Send event!" << endl;
}
int main()
{
Model m;
m.ChangeMethod1();
m.ChangeMethod2();
m.ChangeMethod3();
m.ChangeMethod4();
return 0;
}
And here is the output of the demo C++ code:
Send event!
Send event!
Send event!
Send event!
You see that in the main() function, I have only 4 function calls, and only 4 events is send.
The method I use is that I put a DeferredEventSender local object in each ChangeMethodX method, and if it is a top level function call, this object will have its member variable m_TopLevelCallScope set as true, if it is a inner function call, m_TopLevelCallScope is set as false.
When the DeferredEventSender local object leaves the scope, it will check to see if it is the top level object, if true, it will send the event, so all the inner function calls won't send events.
The demo code can be extended, so that events can be accumulated and stored in a std::queue<Event> in the DeferredEventSender object or Model object, and when the top DeferredEventSender object get destroyed, we can run a filter in the std::queue<Event>, and remove the duplicated events, and send the events we actually needed.
As suggested by Marco Luzzara, this is the modified version, thanks Marco Luzzara!
Revision 2:
#include <iostream>
using namespace std;
class Model
{
public:
Model()
{
}
~Model()
{
}
void ChangeMethod1();
void ChangeMethod2();
void ChangeMethod3();
void ChangeMethod4();
void Notify();
protected:
class DeferredEventSender
{
public:
DeferredEventSender(Model* m)
{
m_Model = m;
// the first instance of the DeferredEventSender will copy the status of m_TopLevel
// and all the later(inner) instances will have false m_TopLevel
m_DoCallNotify = m_Model->m_TopLevel;
m_Model->m_TopLevel = false;
}
~DeferredEventSender()
{
// we only call Notify on the top level DeferredEventSender
if (m_DoCallNotify)
{
m_Model->Notify();
m_Model->m_TopLevel = true;
}
}
Model* m_Model;
bool m_DoCallNotify;
};
bool m_TopLevel = true;
int m_A;
int m_B;
};
void Model::ChangeMethod1()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 1;
}
void Model::ChangeMethod2()
{
Model::DeferredEventSender sender(this);
m_A = m_A + 2;
}
void Model::ChangeMethod3()
{
Model::DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
}
void Model::ChangeMethod4()
{
Model::DeferredEventSender sender(this);
ChangeMethod1();
ChangeMethod2();
ChangeMethod3();
}
void Model::Notify()
{
cout << "Send event!" << endl;
}
int main()
{
Model m;
m.ChangeMethod1();
m.ChangeMethod2();
m.ChangeMethod3();
m.ChangeMethod4();
return 0;
}

C++ return different objects

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();
}
}
};

Why does this exists prematurely with a Sigill signal?

I have a problem in which I have to return the name of a winner in a fight between two fighters.
The class for fighter is as follows:
class Fighter
{
private:
std::string name;
int health;
int damagePerAttack;
public:
Fighter(std::string name, int health, int damagePerAttack)
{
this->name = name;
this->health = health;
this->damagePerAttack = damagePerAttack;
}
~Fighter() { };
std::string getName()
{
return name;
}
int getHealth()
{
return health;
}
int getDamagePerAttack()
{
return damagePerAttack;
}
void setHealth(int value)
{
health = value;
}
};
I wrote a function that should return the name of the winner.
std::string declareWinner(Fighter* fighter1, Fighter* fighter2,
std::string firstAttacker)
{
// Your code goes here. Have fun!
if(firstAttacker==fighter1->getName())
{
while(fighter1->getHealth()!=0&&fighter2->getHealth()!=0)
{
fighter2->setHealth(fighter2->getHealth()-fighter1->getDamagePerAttack());
if(fighter2->getHealth()<=0)
{
return fighter1->getName();
}
fighter1->setHealth(fighter1->getHealth()-fighter2->getDamagePerAttack());
if(fighter1->getHealth()<=0)
{
return fighter2->getName();
}
}
}
else if(firstAttacker==fighter2->getName())
{
while(fighter1->getHealth()!=0&&fighter2->getHealth()!=0)
{
fighter1->setHealth(fighter1->getHealth()-fighter2->getDamagePerAttack());
if(fighter1->getHealth()<=0)
{
return fighter2->getName();
}
fighter2->setHealth(fighter2->getHealth()-fighter1->getDamagePerAttack());
if(fighter2->getHealth()<=0)
{
return fighter1->getName();
}
}
}
}
This satisfies all my needs, but it throws SIGILL signal, and i do not know what i did wrong. How should I deal with it?
In some conditions, it is possible that function runs to the end and exits without returning a value, that corrupts stack and can lead to SIGILL. As a safe measure you can, for example, add return statement to the end of function.
std::string declareWinner(Fighter* fighter1, Fighter* fighter2,
std::string firstAttacker)
{
// Your code goes here. Have fun!
if(firstAttacker==fighter1->getName())
{
while(fighter1->getHealth()!=0&&fighter2->getHealth()!=0)
{
fighter2->setHealth(fighter2->getHealth()-fighter1->getDamagePerAttack());
if(fighter2->getHealth()<=0)
{
return fighter1->getName();
}
fighter1->setHealth(fighter1->getHealth()-fighter2->getDamagePerAttack());
if(fighter1->getHealth()<=0)
{
return fighter2->getName();
}
}
}
else if(firstAttacker==fighter2->getName())
{
while(fighter1->getHealth()!=0&&fighter2->getHealth()!=0)
{
fighter1->setHealth(fighter1->getHealth()-fighter2->getDamagePerAttack());
if(fighter1->getHealth()<=0)
{
return fighter2->getName();
}
fighter2->setHealth(fighter2->getHealth()-fighter1->getDamagePerAttack());
if(fighter2->getHealth()<=0)
{
return fighter1->getName();
}
}
}
return "No winner"; <= Add before exiting function
}
I also noticed there's a redundancy and possible logical errors in code.
I would rewrite it like this (without changing function signature):
std::string declareWinner(Fighter* fighter1, Fighter* fighter2,
std::string firstAttacker)
{
Fighter *first;
Fighter *second;
if(firstAttacker == fighter1->getName()) {
first = fighter2;
second = fighter1;
} else if (firstAttacker == fighter2->getName()) {
first = fighter1;
second = fighter2;
} else {
// Bad call parameters
return "Bad call"; // Throw exception maybe?
}
// Simulating fighting
do {
std::swap(second,first);
second->setHealth(second->getHealth() - first->getDamagePerAttack());
} while (second->getHealth() > 0);
return first->getName();
}

How can I simplify this "variable as template parameter" in C++?

How can I simplify this Code?
mfer::i_value* make_empty_value(mfer::tag tag_)
{
if (tag_ == mfer::tag::mwf_ble) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ble>());
} else if (tag_ == mfer::tag::mwf_chn) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_chn>());
} else if (tag_ == mfer::tag::mwf_blk) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_blk>());
} else if (tag_ == mfer::tag::mwf_seq) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_seq>());
} else if (tag_ == mfer::tag::mwf_man) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_man>());
} else if (tag_ == mfer::tag::mwf_ivl) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_ivl>());
} else if (tag_ == mfer::tag::mwf_sen) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_sen>());
} else if (tag_ == mfer::tag::mwf_wfm) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_wfm>());
} else if (tag_ == mfer::tag::mwf_pre) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pre>());
} else if (tag_ == mfer::tag::mwf_off) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_off>());
} else if (tag_ == mfer::tag::mwf_nul) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nul>());
} else if (tag_ == mfer::tag::mwf_pnt) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnt>());
} else if (tag_ == mfer::tag::mwf_nte) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_nte>());
} else if (tag_ == mfer::tag::mwf_txc) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_txc>());
} else if (tag_ == mfer::tag::mwf_flt) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_flt>());
} else if (tag_ == mfer::tag::mwf_skw) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_skw>());
} else if (tag_ == mfer::tag::mwf_mss) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_mss>());
} else if (tag_ == mfer::tag::mwf_pnm) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pnm>());
} else if (tag_ == mfer::tag::mwf_pid) {
return memory_manager::instance().add(new mfer::t_value<mfer::tag::mwf_pid>());
}
return nullptr;
}
Briefly stating,
mfer::tag is enumeration, defined like enum tag {}; in namespace mfer.
mfer::i_value is abstract class.
class i_value {};
mfer::t_value is templated class like,
template <mfer::tag tag_type>
class t_value : public i_value {};
At this moment, I don't know how to simplify this make_empty_value().
Ideally, I want to make it like this:
mfer::i_value* make_empty_value(mfer::tag tag_)
{
return memory_manager::instance().add(new mfer::t_value<tag_>());
}
But I know that it is template, so above one doesn't make sense.
Is there any idea simplify this code? (Some modern C++ features, Boost libraries, and so on)
With a little template work, we can get the factory function down to:
i_value* make_empty_value(tag tag_type)
{
static constexpr auto factory = make_factory(all_tags());
auto index = std::size_t(tag_type - tag::first);
if (index < tag::ntags) {
return memory_manager::instance().add(factory[index]());
}
else {
return nullptr;
}
}
Full code below.
The i_value generator map is built at compile time, allowing constant-time lookup.
constraints:
the values in the enum must be consecutive, but they need not begin at zero.
this demo requires c++14. It can be easily adapted to work with c++11. For c++03 we'd want to reach out to boost mpl or boost_pp.
complete working example:
#include <array>
#include <utility>
#include <deque>
#include <iostream>
// minimal implementation of virtual base
class i_value {
public:
virtual void prove() const = 0;
virtual ~i_value() = default;
};
// tag enum - note that we have supplied some extra introspection information
// these could just as well be constexpr integers outside the enum
enum tag
{
ble,
chn,
blk,
seq,
first = ble, // first available tag
last = seq, // last available tag
ntags = last-first // number of tags
};
/// Function to offset an index sequence by the distance from
/// zero to the first available tag - in case the first tag is not zero
template<std::size_t...tags>
constexpr auto tag_offset(std::index_sequence<tags...>)
{
return std::index_sequence<(tags + tag::first)...>();
}
/// Function to compute an index sequence of all valid tags
constexpr auto all_tags()
{
return tag_offset(std::make_index_sequence<std::size_t(ntags)>());
}
/// Factory function to generate a derived class for a given tag
template <tag tag_type>
class t_value : public i_value {
void prove() const override { void(std::cout << "I have tag " << tag_type << std::endl); }
~t_value() { void(std::cout << "tag " << tag_type << " destroyed" << std::endl); }
};
template<tag tag_type>
i_value* make_instance()
{
return new t_value<tag_type>();
}
/// Function to generate a 'factory' - an array of factory functions, one for
/// each tag in the variadic template argument tags...
/// Note that the array is zero-based, the tags may not be. All we care about
/// here is the size of the list of tags (and their values)
///
template<std::size_t...tags>
constexpr auto make_factory(std::index_sequence<tags...>)
{
return std::array<i_value* (*)(), sizeof...(tags)>
{
&make_instance<static_cast<tag>(tags)>...
};
}
// minimal memory manager
struct memory_manager {
struct impl {
i_value* add(i_value* item) {
_ivalues.push_back(item);
return item;
};
~impl() {
for (auto i = _ivalues.rbegin() ; i != _ivalues.rend() ; ++i) {
delete *i;
}
}
std::deque<i_value*> _ivalues;
};
static impl& instance()
{
static impl _instance = {};
return _instance;
}
};
// here is resulting factory function.
i_value* make_empty_value(tag tag_type)
{
static constexpr auto factory = make_factory(all_tags());
auto index = std::size_t(tag_type - tag::first);
if (index < tag::ntags) {
return memory_manager::instance().add(factory[index]());
}
else {
return nullptr;
}
}
// test
int main()
{
for(auto tag_type : { tag::ble, tag::chn })
{
auto pvalue = make_empty_value(tag_type);
pvalue->prove();
}
}
expected output:
I have tag 0
I have tag 1
tag 1 destroyed
tag 0 destroyed
You can map the tags to a factory method;
typedef std::unordered_map<mfer::tag,std::function<mfer::i_value*()>> TagMap;
TagMap create_tag_map()
{
TagMap map;
map[mfer::tag::mwf_ble] = [](){ return new mfer::t_value<mfer::tag::mwf_ble>(); };
map[mfer::tag::mwf_chn] = [](){ return new mfer::t_value<mfer::tag::mwf_chn>(); };
map[mfer::tag::mwf_blk] = [](){ return new mfer::t_value<mfer::tag::mwf_blk>(); };
//...
return map;
}
The create_empty_value method could then look like this:
mfer::i_value* make_empty_value(mfer::tag tag_)
{
static TagMap factory = create_tag_map();
auto it = factory.find( tag_ );
if( it != factory.end() )
{
return memory_manager::instance().add( it->second() );
}
return nullptr;
}
see simplified version Live on Coliru
You can create a recursive template function if the enumerate value follows a known pattern (by default next enumerate value equals previous enumerate +1):
//anonymous namespace to "help innliner"
namespace{
//This function return the next enumerates value:
constexpr mref::tag next_tag(mref::tag tag_) {
return static_cast<mref::tag>(
static_cast<std::underlying_type_t<mref::tag>>(tag_) + 1);
}
//The compute function is wrapped in a structure to enable template
//specialization:
template <mref::tag Tag> struct add_to_mem_manager {
static mfer::i_value* compute(mref::tag tag_) {
if (Tag == tag_) {
return memory_manager::instance().add(
new mfer::t_value<Tag>());
} else {
return add_to_mem_manager<next_tag(Tag)>::compute(tag_);
}
}
};
//Specialization for last enumerate
template <> struct add_to_mem_manager<mfer::tag::mwf_pid> {
static mref::ivalue* compute(mref::tag tag_) {
assert(mref::tag::mwf_pid == tag_);
return memory_manager::instance().add(
new mfer::t_value<mfer::tag::mwf_pid>());
}
};
}
mfer::i_value* make_empty_value(mfer::tag tag_){
//call with template parameter equals to the
//the enumerate whose values is the smallest
return add_to_mem_manager<mfer::tag::mwf_ble>::compute(tag_);
}
If you don't know the constitutive rule of your enumerate, you cannot do this,( generaly constitutive law is as in this example, x[i+1]=x[i]+1, or x[i+1]=x[i]<<1 (left shift).) Otherwise their is no way to iterate over elements of an enumeration.
Note: The function compute will certainly be inlined, but in doubt you can use
compiler specific attribute as __forceinline with MSVC or __attribute__((__always_inline__)) with GCC or clang.
Not directly using your example, but you can do something on the below lines, i.e converting enum to a type.
enum Type {
Type_A,
Type_B,
};
template <Type T>
struct Enum2Type {
constexpr static const Type type = T;
};
template <typename T>
mfer::i_value* make_empty_value(T tag_type)
{
return memory_manager::instance().add(new mfer::t_value<tag_type.type>());
}
auto val = make_empty_value(Enum2Type<Type_A>());
auto val2 = make_empty_value(Enum2Type<Type_B>());
The only scope of simplification I see is in removing the boilerplate code by replacing with a fixed macro. This will be soothing to the viewer.
Instead of so many if-else if, make it a switch/case as below:
#define CASE(TAG) \
case TAG: return memory_manager::instance().add(new mfer::t_value<TAG>())
mfer::i_value* make_empty_value(const mfer::tag tag_)
{
switch(tag_) {
{
CASE(mfer::tag::mwf_ble);
CASE(mfer::tag::mwf_chn);
CASE(mfer::tag::mwf_blk);
//...
default: break;
}
return nullptr;
}
#undef CASE