C++:监视多个值类型

C++: Monitoring multiple value types

我想写一个class,可以监控一堆不同的值,方便调试。想象一下在可视化调试器中设置 "watches"。我正在想象这样的事情:

struct Foo {
    int x = 0;
    std::string s = "bar";
};

int main() {
    Foo f;
    ValueMonitor::watch("number", &f.x);
    ValueMonitor::watch("string", &f.s);
    for (int i = 0; i < 10; ++i) {
        ++f.x;
        if (i > 5) {
            f.s = "new string";
        }

        // print the current value of the variable with the given key
        // these should change as the loop goes on
        ValueMonitor::print("number");
        ValueMonitor::print("string");
        // or
        ValueMonitor::printAll();

        // obviously this would be unnecessary in this example since I
        // have easy access to f, but imagine monitoring different
        // values from all over a much larger code base
    }
}

然后可以在应用程序的 GUI 或其他地方轻松监控这些。

但是,我不知道如何处理将存储在此 class 中的不同类型。理想情况下,我应该能够存储任何具有字符串表示形式的内容。我有一些想法,但其中 none 似乎是正确的:

  1. 存储指向定义 toString 函数或 operator<< 的 superclass 的指针,如 Java 的 Object。但这需要我为我想要监视的任何原语制作包装器。
  2. 类似于 boost::anyboost::spirit::hold_any。我认为 any 需要在打印之前进行类型转换...我想我可以 try/catch 转换为一堆不同的类型,但那会很慢。 hold_any 需要定义的流运算符,这将是完美的......但我无法让它与指针一起工作。

有人有什么想法吗?

我在别处找到了解决办法。我很震惊,所以不妨 post 把它放在这里以供将来参考。它看起来像这样:

class Stringable
{
public:
    virtual ~Stringable() {};
    virtual std::string str() const = 0;

    using Ptr = std::shared_ptr<Stringable>;
};

template <typename T>
class StringableRef : public Stringable
{
private:
    T* _ptr;

public:
    StringableRef(T& ref)
        : _ptr(&ref) {}
    virtual ~StringableRef() {}

    virtual std::string str() const
    {
        std::ostringstream ss;
        ss << *_ptr;
        return ss.str();
    }
};

class ValueMonitor
{
private:
    static std::map<std::string, Stringable::Ptr> _values;

public:
    ValueMonitor() {}
    ~ValueMonitor() {}

    template <typename T>
    static void watch(const std::string& label, T& ref)
    {
        _values[label] = std::make_shared<StringableRef<T>>(ref);
    }

    static void printAll()
    {
        for (const auto& valueItr : _values)
        {
            const String& name = valueItr.first;
            const std::shared_ptr<Stringable>& value = valueItr.second;
            std::cout << name << ": " << value->str() << std::endl;
        }
    }

    static void clear()
    {
        _values.clear();
    }
};

std::map<std::string, Stringable::Ptr> ValueMonitor::_values;

.

int main()
{
    int i = 5;
    std::string s = "test"
    ValueMonitor::watch("number", i);
    ValueMonitor::watch("string", s);
    ValueMonitor::printAll();

    i = 10;
    s = "new string";
    ValueMonitor::printAll();

    return 0;
}