[英]Using std::type_info for casting in natvis
在我正在工作的代码库中,我们使用std::any
而不是void*
来通过一些通用的非模板代码传递类。 具体来说,我们使用 Visual Studio 2019、它的编译器和标准库。
为了可视化std::any
,微软已经给出了一个 natvis:
<Type Name="std::any">
<Intrinsic Name="has_value" Expression="_Storage._TypeData != 0"/>
<Intrinsic Name="_Rep" Expression="_Storage._TypeData & _Rep_mask"/>
<Intrinsic Name="type" Expression="(const type_info*)(_Storage._TypeData & ~_Rep_mask)"/>
<Intrinsic Name="_Is_trivial" Expression="has_value() && _Rep() == 0"/>
<Intrinsic Name="_Is_big" Expression="has_value() && _Rep() == 1"/>
<Intrinsic Name="_Is_small" Expression="has_value() && _Rep() == 2"/>
<DisplayString Condition="!has_value()">[empty]</DisplayString>
<DisplayString Condition="_Is_trivial() || _Is_small()">[not empty (Small)]</DisplayString>
<DisplayString Condition="_Is_big()">[not empty (Large)]</DisplayString>
<Expand>
<Synthetic Name="has_value">
<DisplayString>{has_value()}</DisplayString>
</Synthetic>
<Synthetic Name="type" Condition="has_value()">
<DisplayString>{type()}</DisplayString>
</Synthetic>
<Synthetic Name="[representation]" Condition="_Is_trivial()">
<DisplayString>(Small/Trivial Object)</DisplayString>
</Synthetic>
<Synthetic Name="[representation]" Condition="_Is_small()">
<DisplayString>(Small Object)</DisplayString>
</Synthetic>
<Synthetic Name="[representation]" Condition="_Is_big()">
<DisplayString>(Dynamic Allocation)</DisplayString>
</Synthetic>
</Expand>
</Type>
然而,这最终向我们展示了(Small Object)
而不是我们存储在其中的std::string
。 我已经设法用几行额外的行来扩展它以获得指向数据的指针:
<Item Name="[castable_ptr]" Condition="_Is_trivial()">(void*)(&_Storage._TrivialData)</Item>
<Item Name="[castable_ptr]" Condition="_Is_small()">(void*)(&_Storage._SmallStorage._Data)</Item>
<Item Name="[castable_ptr]" Condition="_Is_big()">(void*)(_Storage._BigStorage._Ptr)</Item>
但是,这将数据显示为void*
,您必须手动将其转换为实际类型std::string*
的指针。 然而,这个std::any
实现/可视化也带有一个std::type_info
。 (参见字段:类型)知道我们拥有哪种底层类型。
有没有办法使用这个std::type_info
以便可以将(void*)
替换为实际存储类型的强制转换?
编辑: visual studio 为类型提供的信息示例: {mydll.dll!class std::tuple<__int64,double,double,double> 'RTTI Type Descriptor'} {...}
当显式转换地址时到的std :: TYPE_INFO *,我可以访问_Data
在调试器,包含_DecoratedName
( .?AV?$tuple@_JNNN@std@@
)和_UndecoratedName
( nullptr
)。 不幸的是,我似乎无法找到如何编写利用这些信息的演员表。
我不知道上下文(实施限制)。 如果使用std::any被确定为一个强大的先决条件(我可以理解)并且在 Microsoft 改进本机可视化器之前,下面可能是战术上的快速胜利。
在 natvis xml 配置文件中:
<Synthetic Name="[representation]" Condition="_Is_trivial()">
<!--<DisplayString>(Small/Trivial Object)</DisplayString>-->
<DisplayString>{_Storage._TrivialData._Val}</DisplayString>
</Synthetic>
<Synthetic Name="[representation]" Condition="_Is_small()">
<!--<DisplayString>{*displayStdAnyContent(*this)}</DisplayString>-->
<Expand>
<Item Name="[value]">*displayStdAnyContent(*this)</Item>
</Expand>
</Synthetic>
<Type Name="AnyCastedValue<*>">
<DisplayString>{value}</DisplayString>
<Expand>
<Item Name="[value]">value</Item>
</Expand>
</Type>
C++
//----------------------------------------------------------------------------------
class IAnyValue
{
public:
virtual ~IAnyValue() = default;
};
template <typename T>
struct AnyCastedValue final :public IAnyValue
{
AnyCastedValue(const T& pi_value) :value(pi_value) {}
T value;
};
struct NonCastableValue final :public IAnyValue
{
static constexpr auto message = "I'm not castable! Please register my type!";
};
//----------------------------------------------------------------------------------
class IAnyCaster
{
public:
virtual std::unique_ptr<IAnyValue> castValue(const std::any& pi_any) const = 0;
virtual ~IAnyCaster() = default;
};
template <typename T>
class AnyCaster final :public IAnyCaster
{
public:
AnyCaster() = default;
virtual std::unique_ptr<IAnyValue> castValue(const std::any& pi_any) const override
{
return std::make_unique<AnyCastedValue<T>>(std::any_cast<T>(pi_any));
}
};
//----------------------------------------------------------------------------------
class AnyCasterService final :public std::unordered_map<std::string, std::unique_ptr<IAnyCaster>>
{
public:
AnyCasterService()
{
//here we register the types you would like be able to watch under the debugger
registerType<int>();
registerType<std::string>();
}
std::unique_ptr<IAnyValue> castValue(const std::any& pi_any) const
{
auto im = find(pi_any.type().name());
if (im != end())
{
return im->second->castValue(pi_any);
}
else return std::make_unique<NonCastableValue>();
}
private:
template <typename T>
void registerType()
{
this->insert(std::make_pair(typeid(T).name(),std::make_unique<AnyCaster<T>>()));
}
};
//----------------------------------------------------------------------------------
std::unique_ptr<IAnyValue> displayStdAnyContent(const std::any& pi_any)
{
static const AnyCasterService s_service;
return s_service.castValue(pi_any);
}
我在 VS2017 v15.9.8 下验证了这个提议
请记住,Natvis 只能根据您的需要(显式用户调用)在运行时评估该方法。
std::any l_any;
l_any = 5;
l_any = std::string("it works!");
std::any 将取消输入您的数据。 它必须被视为 std 中 void* 的安全包装。 因此,您无法在运行时通过 natvis 查看其类型化数据。
我的建议是重新设计你的实现,通过依赖模板类型的具体类继承的通用接口来避免 std::any 或 void* 。 例如:
class IData //base interface
{
public:
~virtual IData()=default;
}
template <typename T>
class TypedData:public IData //abstract or concrete
{
//...
T m_data;
}
void genericFunction(const IData& pi_data); //instead of void* or std::any
int main()
{
TypedData<std::string> l_data("my typed data");
genericFunction(l_data);
//...
}
通过这种设计,您可以定义单个 natvis 条目来管理 TypedData
<Type Name="TypedData<*>"> //TypedData<*>
<DisplayString>{m_data}</DisplayString>
</Type>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.