检测方法是否已被覆盖

时间:2014-03-23 17:56:04

标签: c++ inheritance reflection polymorphism

问题

给定一个C ++基类指针,有没有办法检测是否已覆盖某个虚方法?

背景

我正在为一门小语言写一个翻译。我有一个基类来表示具有各种值类型的特化的值。这是一个简单的例子:

class Value {
  public:
    virtual bool CanBeString() const { return false; }
    virtual std::string GetAsString() const {
      throw std::logic_error("Value cannot be represented as a string.");
    }

    virtual bool CanBeInt() const { return false; }
    virtual int GetAsInt() const {
      throw std::logic_error("Value cannot be represented as an int.");
    }
};

class StringValue : public Value {
  public:
    bool CanBeString() const override { return true; }
    std::string GetAsString() const override { return m_string; }
  private:
    std::string m_string;
};

class IntValue : public Value {
   public:
     // Even though this is an integer, it can be represented as a string.
     bool CanBeString() const override { return true; }
     std::string GetAsString() const override { /* return string rep of m_int */ }

     bool CanBeInt() const override { return true; }
     int GetAsInt() const override { return m_int; }
   private:
     int m_int;
};

解释器使用指向Value的指针,并在运行时进行类型检查。假设解释器有一个值,它需要对它执行一个仅适用于int的操作。它将首先检查pValue->CanBeInt()。如果为true,则解释器继续pValue->GetAsInt()并进行处理。如果该值与int不兼容,则报告类型错误。

永远不应执行GetAsXxx的基本实现。如果执行了一个,则表示解释器中存在错误(它忘记首先检查类型)。那些抛出的声明是用来指示我修复解释器的;我 not 想要抛出异常,因为代码中的类型错误被解释

动机

为了更容易(并且不易出错)向系统添加新类型,我一直在尝试确定是否有办法消除派生类覆盖的需要CanBeXxx能够检测是否已覆盖相应的GetAsXxx方法。

我尝试过什么

我的具体想法是将CanBeXxx方法更改为基类中定义的非虚方法,尝试将GetAsXxx方法的成员函数指针与Value::GetAsXxx方法的成员函数指针进行比较}。如:

bool CanBeInt() const { return &GetAsInt != &Value::GetAsInt; }
唉,这不会编译,因为很明显你无法获得已经绑定的方法的成员函数指针。这个想法或其他方法是否有变化可以允许这一小部分反射?

1 个答案:

答案 0 :(得分:1)

拉开成员方法指针以进行比较是特定于编译器的,因为不同的编译器以不同方式处理方法指针。但是,您可以考虑使用其他设计。它不会完全消除这个问题,但它会简化一点,同时仍然可以灵活地在未来添加更多类型:

const unsigned int CanBeInt = 1;
const unsigned int CanBeString = 2;
...

class Value {
  private:
    unsigned int flags;
  public:
    Value(unsigned int aflags) : flags(aflags) {}

    unsigned int GetFlags() const { return flags; }

    inline bool CanBeString() const { return (flags & CanBeString); }
    virtual std::string GetAsString() const {
      throw std::logic_error("Value cannot be represented as a string.");
    }

    inline bool CanBeInt() const { return (flags & CanBeInt); }
    virtual int GetAsInt() const {
      throw std::logic_error("Value cannot be represented as an int.");
    }
};

class StringValue : public Value {
  public:
    StringValue() : Value(CanBeString) {}
    std::string GetAsString() const override { return m_string; }
  private:
    std::string m_string;
};

class IntValue : public Value {
   public:
     // Even though this is an integer, it can be represented as a string.
     IntValue() : Value(CanBeInt | CanBeString) {}
     std::string GetAsString() const override { /* return string rep of m_int */ }
     int GetAsInt() const override { return m_int; }
   private:
     int m_int;
};

if (pValue->CanBeInt()) {
    int val = pValue->GetAsInt();
    ...
}


if (pValue->GetFlags() & CanBeInt) {
    int val = pValue->GetAsInt();
    ...
}

if (pValue->CanBeString()) {
   std::string val = pValue->GetAsString();
   ...
}

if (pValue->GetFlags() & CanBeString) {
   std::string val = pValue->GetAsString();
   ...
}

另一个选项,如@Beta建议:

class Value {
  public:
    virtual bool GetAsString(std::string &) const { return false; }
    virtual bool GetAsInt(int &) const { return false; }
};

class StringValue : public Value {
  public:
    bool GetAsString(std::string &value) const override { value = m_string; return true; }
  private:
    std::string m_string;
};

class IntValue : public Value {
   public:
     // Even though this is an integer, it can be represented as a string.
     bool GetAsString(std::string &value) const override { value = ...; return true; }
     bool GetAsInt(int &value) const override { value = m_int; return true; }
   private:
     int m_int;
};

std::string val;
if (!pValue->GetAsString(val))
   throw std::logic_error("Value cannot be represented as a string.");

int val;
if (!pValue->GetAsInt(val))
   throw std::logic_error("Value cannot be represented as a int.");