How to make the current coding structure more flexible

那年仲夏 提交于 2021-01-01 09:38:29

问题


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:

  1. A std::map to link the input text with the desired behaviour.
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!