在抽象基础 class 中使用 typeid 自动生成派生的 class 名称。如何得到想要的结果?
Using typeid in an abstract base class to auto generate derived class names. How to get desired results?
我有这个示例程序可以运行,但没有产生预期的结果...
这是当前输出:
输出
class Component_00 class Component_01 class Component_02
Successfully connected class Component_01 to class Component_00
Successfully connected class Component_02 to class Component_00
Component class Component_02 already exists in class Component_00!
Successfully connected class Component_02 to class Component_01
这将是我想要的输出:
Wire_00 Wire_01 Wire_02
Successfully connected Wire_01 to Wire_00
Successfully connected Wire_02 to Wire_00
Component Wire_02 already exists in Wire_00!
Successfully connected Wire_02 to Wire_01
这是一个双重问题,但它们与同一个问题相关...
第一部分是我不想在实际 class 名称之前打印 class
这个词。
第二部分是我不想打印基础 class 的名称,因为这是一个摘要 class。我想打印派生的 class 的名字...
我需要做什么来解决这个问题?
-注意- 我正在使用 Visual Studio 2017,但这应该与所使用的任何编译器无关,它应该是可移植的。
这是我所有的相关源代码:
main.cpp
#include <iostream>
#include <exception>
#include "Wire.h"
int main() {
try {
Wire w1, w2, w3;
std::cout << w1.id() << " " << w2.id() << " " << w3.id() << "\n";
w1.connect(&w2);
w1.connect(&w3);
w1.connect(&w3);
w2.connect(&w3);
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Component.h
#pragma once
#include <list>
#include <map>
#include <memory>
#include <string>
#include <typeinfo>
class Component {
private:
std::string id_ = std::string( typeid(*this).name() ) + "_0";
std::list<Component*> components_;
public:
Component() {
updateId();
}
virtual ~Component() {}
std::string& id() { return id_; }
void connect(Component* other) {
for (auto& l : components_) {
if (other->id_ == l->id()) {
std::cout << "Component " << other->id_ << " already exists in " << id_ << "!\n";
return;
}
}
components_.emplace_back( other );
std::cout << "Successfully connected " << other->id() << " to " << id_ << "\n";
}
virtual void propagate() = 0;
private:
void updateId() {
static int i = 0;
id_.append( std::to_string(i++) );
}
};
Wire.h
#pragma once
#include "Component.h"
class Wire : public Component {
private:
public:
Wire() {}
virtual ~Wire() {}
virtual void propagate() override {
return;
}
};
编辑
我修改了我的基础并派生了 class 以使用辅助函数生成 class 名称...
这里是修改后的 classes:
Component.h
#pragma once
#include <list>
#include <map>
#include <memory>
#include <string>
#include <typeinfo>
template<class T>
constexpr const std::string generateName(T t) {
return std::string(typeid(t).name());
}
class Component {
protected:
std::string id_ = "";
std::list<Component*> components_;
public:
explicit Component(const std::string& id) : id_{ id } {}
virtual ~Component() {}
std::string& id() { return id_; }
void connect(Component* other) {
for (auto& l : components_) {
if (other->id_ == l->id()) {
std::cout << "Component " << other->id_ << " already exists in " << id_ << "!\n";
return;
}
}
components_.emplace_back( other );
std::cout << "Successfully connected " << other->id() << " to " << id_ << "\n";
}
virtual void propagate() = 0;
protected:
void updateId() {
static int i = 0;
id_.append( "_" + std::to_string(i++) );
}
};
Wire.h
#pragma once
#include "Component.h"
class Wire : public Component {
private:
public:
Wire() : Component(generateName(this)) {
updateId();
};
virtual ~Wire() {}
virtual void propagate() override {
return;
}
};
主cpp文件没有改变...
这是新的输出...
class Wire *_0 class Wire *_1 class Wire *_2
Successfully connected class Wire *_1 to class Wire *_0
Successfully connected class Wire *_2 to class Wire *_0
Component class Wire *_2 already exists in class Wire *_0!
Succesfully connected class Wire *_2 to class Wire *_1
这现在给了我想要的基础 class 的名字,但是,它仍然在它之前打印我不想要的世界 class
,现在它也是附加一个 space 后跟一个 *
其中我也不想要...
以下用于获取类型字符串的代码(从我的 RareCpp 库中提取)适用于 VS、gcc 和 Clang,据我所知目前还没有完全可移植的解决方案,typeid 不保证生成用户友好的类型名称。
template <typename T>
constexpr auto getTypeView()
{
std::string_view view;
#ifdef _MSC_VER
#ifndef __clang__
view = __FUNCSIG__;
view.remove_prefix(view.find_first_of("<")+1);
view.remove_suffix(view.size()-view.find_last_of(">"));
#else
view = __PRETTY_FUNCTION__;
view.remove_prefix(view.find_first_of("=")+1);
view.remove_prefix(view.find_first_not_of(" "));
view.remove_suffix(view.size()-view.find_last_of("]"));
#endif
#else
#ifdef __clang__
view = __PRETTY_FUNCTION__;
view.remove_prefix(view.find_first_of("=")+1);
view.remove_prefix(view.find_first_not_of(" "));
view.remove_suffix(view.size()-view.find_last_of("]"));
#else
#ifdef __GNUC__
view = __PRETTY_FUNCTION__;
view.remove_prefix(view.find_first_of("=")+1);
view.remove_prefix(view.find_first_not_of(" "));
view.remove_suffix(view.size()-view.find_last_of("]"));
#else
view = "unknown";
#endif
#endif
#endif
return view;
}
template <typename T>
struct TypeName
{
constexpr TypeName() : value() {
auto view = getTypeView<T>();
for ( size_t i=0; i<view.size(); i++ )
value[i] = view[i];
value[view.size()] = '[=10=]';
}
char value[getTypeView<T>().size()+1];
};
template <typename T>
std::string TypeToStr() {
return std::string(TypeName<T>().value);
}
然后您可以删除 struct/class 关键字和额外的空格,例如
std::string simplifyTypeStr(const std::string & typeStr) {
std::string rawSimpleTypeStr = typeStr;
if ( rawSimpleTypeStr.find("struct ", 0) != std::string::npos )
rawSimpleTypeStr.erase(0, strlen("struct "));
if ( rawSimpleTypeStr.find("class ", 0) != std::string::npos )
rawSimpleTypeStr.erase(0, strlen("class "));
std::string simpleTypeStr;
for ( size_t i=0; i<rawSimpleTypeStr.size(); i++ ) {
if ( rawSimpleTypeStr[i] != ' ' )
simpleTypeStr += rawSimpleTypeStr[i];
else if ( ++i < rawSimpleTypeStr.size() ) /* Remove space and upper-case the letter following the space */
simpleTypeStr += std::toupper(rawSimpleTypeStr[i]);
}
return simpleTypeStr;
}
并通过使用 std::remove_pointer 从类型中删除指针来删除指针,您从 generate name 调用看起来像...
template<class T>
constexpr const std::string generateName(T t) {
return simplifyTypeStr(TypeToStr<std::remove_pointer_t<T>>());
}
将其复制到您的其余代码中,我得到了输出...
Wire_0 Wire_1 Wire_2
Successfully connected Wire_1 to Wire_0
Successfully connected Wire_2 to Wire_0
Component Wire_2 already exists in Wire_0!
Successfully connected Wire_2 to Wire_1
编辑:一定要#include
并确保您的编译器设置为 C++17 或更高版本(在 VS 中,这是在项目->属性下,“C++ 语言标准”,将其设置为 ISO C++17)
我有这个示例程序可以运行,但没有产生预期的结果...
这是当前输出:
输出
class Component_00 class Component_01 class Component_02
Successfully connected class Component_01 to class Component_00
Successfully connected class Component_02 to class Component_00
Component class Component_02 already exists in class Component_00!
Successfully connected class Component_02 to class Component_01
这将是我想要的输出:
Wire_00 Wire_01 Wire_02
Successfully connected Wire_01 to Wire_00
Successfully connected Wire_02 to Wire_00
Component Wire_02 already exists in Wire_00!
Successfully connected Wire_02 to Wire_01
这是一个双重问题,但它们与同一个问题相关...
第一部分是我不想在实际 class 名称之前打印 class
这个词。
第二部分是我不想打印基础 class 的名称,因为这是一个摘要 class。我想打印派生的 class 的名字...
我需要做什么来解决这个问题?
-注意- 我正在使用 Visual Studio 2017,但这应该与所使用的任何编译器无关,它应该是可移植的。
这是我所有的相关源代码:
main.cpp
#include <iostream>
#include <exception>
#include "Wire.h"
int main() {
try {
Wire w1, w2, w3;
std::cout << w1.id() << " " << w2.id() << " " << w3.id() << "\n";
w1.connect(&w2);
w1.connect(&w3);
w1.connect(&w3);
w2.connect(&w3);
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Component.h
#pragma once
#include <list>
#include <map>
#include <memory>
#include <string>
#include <typeinfo>
class Component {
private:
std::string id_ = std::string( typeid(*this).name() ) + "_0";
std::list<Component*> components_;
public:
Component() {
updateId();
}
virtual ~Component() {}
std::string& id() { return id_; }
void connect(Component* other) {
for (auto& l : components_) {
if (other->id_ == l->id()) {
std::cout << "Component " << other->id_ << " already exists in " << id_ << "!\n";
return;
}
}
components_.emplace_back( other );
std::cout << "Successfully connected " << other->id() << " to " << id_ << "\n";
}
virtual void propagate() = 0;
private:
void updateId() {
static int i = 0;
id_.append( std::to_string(i++) );
}
};
Wire.h
#pragma once
#include "Component.h"
class Wire : public Component {
private:
public:
Wire() {}
virtual ~Wire() {}
virtual void propagate() override {
return;
}
};
编辑
我修改了我的基础并派生了 class 以使用辅助函数生成 class 名称...
这里是修改后的 classes:
Component.h
#pragma once
#include <list>
#include <map>
#include <memory>
#include <string>
#include <typeinfo>
template<class T>
constexpr const std::string generateName(T t) {
return std::string(typeid(t).name());
}
class Component {
protected:
std::string id_ = "";
std::list<Component*> components_;
public:
explicit Component(const std::string& id) : id_{ id } {}
virtual ~Component() {}
std::string& id() { return id_; }
void connect(Component* other) {
for (auto& l : components_) {
if (other->id_ == l->id()) {
std::cout << "Component " << other->id_ << " already exists in " << id_ << "!\n";
return;
}
}
components_.emplace_back( other );
std::cout << "Successfully connected " << other->id() << " to " << id_ << "\n";
}
virtual void propagate() = 0;
protected:
void updateId() {
static int i = 0;
id_.append( "_" + std::to_string(i++) );
}
};
Wire.h
#pragma once
#include "Component.h"
class Wire : public Component {
private:
public:
Wire() : Component(generateName(this)) {
updateId();
};
virtual ~Wire() {}
virtual void propagate() override {
return;
}
};
主cpp文件没有改变...
这是新的输出...
class Wire *_0 class Wire *_1 class Wire *_2
Successfully connected class Wire *_1 to class Wire *_0
Successfully connected class Wire *_2 to class Wire *_0
Component class Wire *_2 already exists in class Wire *_0!
Succesfully connected class Wire *_2 to class Wire *_1
这现在给了我想要的基础 class 的名字,但是,它仍然在它之前打印我不想要的世界 class
,现在它也是附加一个 space 后跟一个 *
其中我也不想要...
以下用于获取类型字符串的代码(从我的 RareCpp 库中提取)适用于 VS、gcc 和 Clang,据我所知目前还没有完全可移植的解决方案,typeid 不保证生成用户友好的类型名称。
template <typename T>
constexpr auto getTypeView()
{
std::string_view view;
#ifdef _MSC_VER
#ifndef __clang__
view = __FUNCSIG__;
view.remove_prefix(view.find_first_of("<")+1);
view.remove_suffix(view.size()-view.find_last_of(">"));
#else
view = __PRETTY_FUNCTION__;
view.remove_prefix(view.find_first_of("=")+1);
view.remove_prefix(view.find_first_not_of(" "));
view.remove_suffix(view.size()-view.find_last_of("]"));
#endif
#else
#ifdef __clang__
view = __PRETTY_FUNCTION__;
view.remove_prefix(view.find_first_of("=")+1);
view.remove_prefix(view.find_first_not_of(" "));
view.remove_suffix(view.size()-view.find_last_of("]"));
#else
#ifdef __GNUC__
view = __PRETTY_FUNCTION__;
view.remove_prefix(view.find_first_of("=")+1);
view.remove_prefix(view.find_first_not_of(" "));
view.remove_suffix(view.size()-view.find_last_of("]"));
#else
view = "unknown";
#endif
#endif
#endif
return view;
}
template <typename T>
struct TypeName
{
constexpr TypeName() : value() {
auto view = getTypeView<T>();
for ( size_t i=0; i<view.size(); i++ )
value[i] = view[i];
value[view.size()] = '[=10=]';
}
char value[getTypeView<T>().size()+1];
};
template <typename T>
std::string TypeToStr() {
return std::string(TypeName<T>().value);
}
然后您可以删除 struct/class 关键字和额外的空格,例如
std::string simplifyTypeStr(const std::string & typeStr) {
std::string rawSimpleTypeStr = typeStr;
if ( rawSimpleTypeStr.find("struct ", 0) != std::string::npos )
rawSimpleTypeStr.erase(0, strlen("struct "));
if ( rawSimpleTypeStr.find("class ", 0) != std::string::npos )
rawSimpleTypeStr.erase(0, strlen("class "));
std::string simpleTypeStr;
for ( size_t i=0; i<rawSimpleTypeStr.size(); i++ ) {
if ( rawSimpleTypeStr[i] != ' ' )
simpleTypeStr += rawSimpleTypeStr[i];
else if ( ++i < rawSimpleTypeStr.size() ) /* Remove space and upper-case the letter following the space */
simpleTypeStr += std::toupper(rawSimpleTypeStr[i]);
}
return simpleTypeStr;
}
并通过使用 std::remove_pointer 从类型中删除指针来删除指针,您从 generate name 调用看起来像...
template<class T>
constexpr const std::string generateName(T t) {
return simplifyTypeStr(TypeToStr<std::remove_pointer_t<T>>());
}
将其复制到您的其余代码中,我得到了输出...
Wire_0 Wire_1 Wire_2
Successfully connected Wire_1 to Wire_0
Successfully connected Wire_2 to Wire_0
Component Wire_2 already exists in Wire_0!
Successfully connected Wire_2 to Wire_1
编辑:一定要#include