在抽象基础 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)