在C ++

时间:2015-09-15 22:00:37

标签: c++ c++11

我有这个例子:

class Entity
{
    float mX;
    float mY;
    string mName;
    // other attributes
public:
    void setPosX(float x);
    void setPosY(float y);
    void setName(string name);
    // other setters

    template<typename T>
    virtual void setAttributeValue(string param, T value)
    {
        if(param == "x")
            setX(value);
        else if(param == "y")
            setY(value);
        else if(param == "name")
            setName(value);
        // ...
    }
};

class SpecialEntity : public Entity
{
    int specialAttr;
    // other special attributes
public:
    void setSpecialAttr(int val);
    // other setters

    template<typename T>
    virtual void setAttributeValue(string param, T value)
    {
        Entity::setAttributeValue(param, value);

        if(param == "specialAttr")
            setSpecialAttr(value);
        // ...
    }
};

由于不允许使用模板化虚拟方法,因此无法编译。

我在我的编辑器应用程序中需要这个,它有一个属性网格控件,根据该控件中属性的名称,我需要从Entity类或继承的Entity类调用一个方法来设置一个属性值。

实现这一目标的最佳方法是什么。

4 个答案:

答案 0 :(得分:2)

当我必须这样做时,我使用的其中一个选项是传递boost::any而不是T

virtual void setAttributeValue(string param, boost::any value)
{
    if(param == "x")
        setX(boost::any_cast<int>(value));
    else if(param == "y")
        setY(boost::any_cast<int>(value));
    else if(param == "name")
        setName(boost::any_cast<std::string>(value));
    // ...
}

答案 1 :(得分:1)

以老式的方式做到这一点

setX(int)
setY(float)
setZ(string)

更安全(编译器会发现错误)和更快

答案 2 :(得分:0)

您无法制作类似虚拟或其他模板的功能。即使您从未打算调用它们,模板函数的每个版本中的所有函数调用都必须编译。

模板函数发生的所有事情都是函数的副本,并根据需要用T替换为每种类型。如果你手工完成,很容易看出它不会编译。在这种情况下,没有setX获取stringsetName没有获得float

class Entity
{
    float mX;
    std::string mName;
public:
    void setX(float x){ mX = x; }
    void setName(std::string name){ mName = name; }

    void setAttributeValue(std::string param, float value)
    {
        if(param == "x")
            setX(value);
        else if(param == "name")
            setName(value);  // Error!
    }
    void setAttributeValue(std::string param, std::string value)
    {
        if(param == "x")
            setX(value);  // Error!
        else if(param == "name")
            setName(value);
    }    
};

我建议你使setAttributeValue虚拟和非模板化,并传入一种可以转换为任何类型的类型。也许是stringboost::anyboost::variant

如果您无法使用boost::anyboost::variant,则可以创建自己的Value接口以传入:

struct Value {
  virtual float getFloat() const = 0;
  virtual std::string getString() const = 0;
  virtual int getInt() const = 0;
};

struct ValueBase : Value {
  float getFloat() const override { throw std::runtime_error("Not float"); }
  std::string getString() const override { throw std::runtime_error("Not string"); }    
  int getInt() const override { throw std::runtime_error("Not int"); }
};

struct FloatValue : ValueBase {
  float value;
  FloatValue(float value) : value(value){}
  float getFloat() const override { return value; }
};

struct StringValue : ValueBase {
  std::string value;
  StringValue(std::string value) : value(value){}
  std::string getString() const override { return value; }
};

struct IntValue : ValueBase {
  int value;
  IntValue(int value) : value(value){}
  int getInt() const override { return value; }
};

class Entity {
    float mX;
    float mY;
    std::string mName;
public:
    void setX(float x);
    void setY(float y);
    void setName(std::string name);
    virtual void setAttributeValue(std::string param, const Value& value) {
        if(param == "x")
            setX(value.getFloat());
        else if(param == "y")
            setY(value.getFloat());
        else if(param == "name")
            setName(value.getString());
    }
};

class SpecialEntity : public Entity {
    int specialAttr;
public:
    void setSpecialAttr(int val);
    void setAttributeValue(std::string param, const Value& value) override {
        Entity::setAttributeValue(param, value);
        if(param == "specialAttr")
            setSpecialAttr(value.getInt());
    }
};

Live demo

答案 3 :(得分:0)

您可以使用奇怪的重复模板模式,多态基类,一些标记和多态值类来实现您所需的功能

首先我们需要一个多态值类型:

$("table td").filter(function(){ return $(this).text() == "lucy" });

然后我们的静态(CRTP)和动态多态性:

struct value{ virtual ~value(){} };

struct text_value: public value{ text_value(const std::string &str_): str(str_){} std::string str; };
struct int_value: public value{ int_value(int i_): i(i_){} int i; };

auto make_value(const std::string &str){ return text_value{str}; }
auto make_value(int i){ return int_value{i}; }

然后我们可以开始定义一些实现我们接口的类! (enum class entity_tag{ text }; class entity{ public: entity(entity_tag tag_): tag(tag_){} virtual ~entity(){} entity_tag tag; }; template<typename Entity> class entity_base: entity{ public: entity_base(): entity(this->get_tag()){} template<typename T> void set_value(const std::string &param, T &&val){ reinterpret_cast<Entity*>(this)->set_value_impl(param, std::forward<T>(val)); } static entity_tag get_tag(){ return Entity::get_tag_impl(); } }; set_value_impl

get_tag_impl

它就像1,2,3一样简单!

您可以按照自己想要的方式使用它:

class text_entity: public entity_base<text_entity>{
    protected:
        std::string font = "times new roman";
        int font_size = 10;
        std::string text = "";

        void set_text(value &&v){
            auto tv = dynamic_cast<text_value&&>(v);
            text = std::move(tv.str);
        }

        void set_font(value &&v){
            auto tv = dynamic_cast<text_value&&>(v);
            font = std::move(tv.str);
        }

        void set_font_size(value &&v){
            auto iv = dynamic_cast<int_value&&>(v);
            font_size = iv.i;
        }

    public:
        static entity_tag get_tag_impl(){ return entity_tag::text; }

        template<typename T>
        void set_value_impl(const std::string &str, T &&val){
            auto v = make_value(val);

            if(str == "text")
                set_text(std::move(v));
            else if(str == "font")
                set_font(std::move(v));
            else if(str == "font_size")
                set_font_size(std::move(v));
            else
                throw "invalid parameter"; 
        }
};

它也不会让你尝试分配,例如int main(){ text_entity text; text.set_value("font", "comic sans"); text.set_value("font_size", 24); text.set_value("text", "Comic sans sucks"); } float,因为"font"没有值类型。如果有,它会在float内抛出异常。

您可以在每个set_font子类中定义value子类,这样如果给出的值对于任何给定参数都不可能,编译器将始终抱怨。

您还可以将实体存储在具有其他实体类型的容器中:

entity

但要访问每个int main(){ std::vector<entity*> entities; text_entity text0; text_entity text1; pic_entity pic0; slider_entity slider0; entities.push_back(&text0); entities.push_back(&text1); entities.push_back(&pic0); entities.push_back(&slider0); } 的{​​{1}}函数,您需要查看与每个变量关联的set_value变量:

entity

您可以为tag类型使用template<typename T> void set_value_of_text(entity *e, const std::string &param, T &&t){ if(e->tag != entity_tag::text) throw "entity is not a text entity"; dynamic_cast<text_entity*>(e)->set_value(param, std::forward<T>(t)); } 以外的其他内容,以便日后添加实体类型

相关问题