问题
I need help with my current workflow , though everthing works as expected but i would like to make the structure more robust.
I have a application where i change the data by string literal.
WAVEFRONT*TREEVIEW*Main$Text*GEOMETRY*TEXT SET TEXT ABCD
as shown in the image where "Main$Text" is the path of the data item the the tree structure.
1) First step-> i have a class to handle the command string and take appropriate action.
int SumCommandInterface::receiveCommand(std::string stdtsrCommand , const QModelIndex & RootIndex, TreeModel *myModel)
{
std::vector<std::string> vectorStrCommand;
std::vector<std::string> vectorStrStarSplit;
std::vector<std::string> vectorStrContainerNames;
boost::algorithm::trim(stdtsrCommand);
boost::split(vectorStrCommand, stdtsrCommand, boost::is_any_of(" "), boost::token_compress_on);
boost::split(vectorStrStarSplit, vectorStrCommand[0], boost::is_any_of("*"), boost::token_compress_on);
if (vectorStrStarSplit[1] == "TREEVIEW") // for tree view
{
if (vectorStrCommand.size() < 4)
return -1;
boost::split(vectorStrContainerNames, vectorStrStarSplit[2], boost::is_any_of("$"), boost::token_compress_on);
TreeItem* itm;
QModelIndex Index = RootIndex;
for (auto &strName : vectorStrContainerNames)
{
if (!getTreeItem(strName, Index, myModel, &itm))
{
return 0;
}
Index = myModel->indexForTreeItem(itm);
}
PluginTypeLookUp lookUp = lookUpPluginMap[vectorStrStarSplit[3]];
Container *cont = itm->GetContainer();
switch (lookUp)
{
case PluginTypeLookUp::GEOMTERY: // This can be function , texture etc, currently only showing code for geometry
{
Geometry *geom = cont->GetGeometry();
if(vectorStrCommand[2] == "TEXT" )
setFontPluginParam< Geometry , std::string >( geom, vectorStrCommand[2], vectorStrCommand[3]);
if (vectorStrCommand[2] == "USESHADOW")
setFontPluginParam< Geometry , bool >(geom, vectorStrCommand[2], (vectorStrCommand[3] == "0" ? false : true) );
if (vectorStrCommand[2] == "FONTSIZE" || vectorStrCommand[2] == "SHADOWDIRECTION")
setFontPluginParam< Geometry, int >(geom, vectorStrCommand[2], std::stoi(vectorStrCommand[3]) );
if (vectorStrCommand[2] == "SHADOWDISTANCE" || vectorStrCommand[2] == "SHADOWOPACITY" || vectorStrCommand[2] == "KERNING" )
setFontPluginParam< Geometry, float >(geom, vectorStrCommand[2], std::stof(vectorStrCommand[3]));
}
break;
case PluginTypeLookUp::TEXTURE:
{
Sum_Texture_2D *texture;
texture = cont->GetTexturePointer();
if(vectorStrCommand[2] == "TEXTUREPATH" )
setTexturePluginParam< Sum_Texture_2D, std::string >(texture, vectorStrCommand[2], vectorStrCommand[3]);
if (vectorStrCommand[2] == "POSITIONX" || vectorStrCommand[2] == "POSITIONY" || vectorStrCommand[2] == "POSITIONZ" || vectorStrCommand[2] == "SCALINGX" || vectorStrCommand[2] == "SCALINGY" || vectorStrCommand[2] == "SCALINGZ" || vectorStrCommand[2] == "ROTATIONX" || vectorStrCommand[2] == "ROTATIONY" || vectorStrCommand[2] == "ROTATIONZ ")
{
setTexturePluginParam< Sum_Texture_2D, float >(texture, vectorStrCommand[2], std::stof(vectorStrCommand[3]));
}
break;
}
default:
break;
}
return 1;
}
return 0;
}
i am passing the geometry parameter in command and based on that i have a if condition.
for example for USESHADOW the command would look like.
WAVEFRONT*TREEVIEW*AMain$Text*GEOMETRY*TEXT SET USESHADOW 1
if Geometry parameter is TEXT the command would look like.
WAVEFRONT*TREEVIEW*AMain$Text*GEOMETRY*TEXT SET TEXT ABC
case PluginTypeLookUp::GEOMTERY: // This can be function , texture
etc, currently only showing code for geometry
{
Geometry *geom = cont->GetGeometry();
if(vectorStrCommand[2] == "TEXT" )
setFontPluginParam< Geometry , std::string >( geom, vectorStrCommand[2], vectorStrCommand[3]);
if (vectorStrCommand[2] == "USESHADOW")
setFontPluginParam< Geometry , bool >(geom, vectorStrCommand[2], (vectorStrCommand[3] == "0" ? false : true) );
if (vectorStrCommand[2] == "FONTSIZE" || vectorStrCommand[2] == "SHADOWDIRECTION")
setFontPluginParam< Geometry, int >(geom, vectorStrCommand[2], std::stoi(vectorStrCommand[3]) );
if (vectorStrCommand[2] == "SHADOWDISTANCE" || vectorStrCommand[2] == "SHADOWOPACITY" || vectorStrCommand[2] == "KERNING" )
setFontPluginParam< Geometry, float >(geom, vectorStrCommand[2], std::stof(vectorStrCommand[3]));
}
break;
The current issue i am facing is that if i have 30 different geometry parameters i would need to add 30 if conditions.
If i add more geometry types i would again need to revisit this code and add more parameters.
can i handle this step in more graceful way.
回答1:
A basic mapped look-up. These make great table-based menu systems and simple parsers. They may not be the fastest, but the code is really simple1, easy to read, and hard to screw up. Adding a new command or keyword can be as simple as adding another line to the map
's initializer.
All you need is:
- A
std::map
to link the input text with the desired behaviour. - A bunch of functions all with the same prototype that execute the desired behaviours. Lambda expressions are a great option when the behavour is simple since they keep all of the related code in one place
Here we go!
#include <map>
#include <functional>
#include <string>
#include <iostream>
#include <vector>
// standing in for setFontPluginParam
template<class T>
void template_func(const std::string& name, const T & data)
{
std::cout << name << ":" << data << std::endl;
}
// functions to convert the data to the correct type for template_func
void text_func(const std::string & name, const std::string & text)
{
std::cout << "text_func "; // make output easier to interpret
template_func(name, text); // call correct template specialization
}
void int_func(const std::string & name, const std::string & num)
{
std::cout << "int_func ";
template_func(name, std::stoi(num));
}
// maps the name to the function. Note: If you're not messing around with lambdas or
// member functions, an old school function pointer may work better that std::function
std::map<std::string, std::function<void(const std::string &, const std::string &)>> call_function =
{
{"TEXT", text_func},
{"INT", int_func},
// same as above, but with a lambda expression to keep everything all in one place
{"DOUBLE", [](const std::string & name, const std::string & num) {
std::cout << "double_func ";
template_func(name, std::stod(num));
}}
};
void demo(const std::vector<std::string> & vectorStrCommand)
{
// uses map to look up correct function, then calls it
// This is basically all that's left of the if/else if chain
call_function[vectorStrCommand[2]](vectorStrCommand[2], vectorStrCommand[3]);
}
int main()
{
demo({"whatever", "whatever", "INT", "42"});
demo({"whatever", "whatever", "TEXT", "I am the very model of a modern major general..."});
demo({"whatever", "whatever", "DOUBLE", "3.14"});
}
1 Because it's simple it works best for simple things. Don't try to write a C++ compiler like this.
来源:https://stackoverflow.com/questions/57882270/how-to-make-the-current-coding-structure-more-flexible