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 似乎是正确的:
- 存储指向定义 toString 函数或 operator<< 的 superclass 的指针,如 Java 的
Object
。但这需要我为我想要监视的任何原语制作包装器。
- 类似于
boost::any
或 boost::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;
}
我想写一个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 似乎是正确的:
- 存储指向定义 toString 函数或 operator<< 的 superclass 的指针,如 Java 的
Object
。但这需要我为我想要监视的任何原语制作包装器。 - 类似于
boost::any
或boost::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;
}