jeudi 20 juillet 2023

C++ Replace enum with instances of class. Avoid heap allocation

I want to replace this enum: enum{ eTagA, eTagB,...}.

With instances of classes. This allows to append new tags, without modifying the enum.
I've got following code:

#include <memory>
#include <list>

struct TagBase {
    bool operator== (const TagBase& other) const {
        return typeid(*this) == typeid(other);
    }

    bool operator!= (const TagBase& other) const {
        return !(*this == other);
    }
    
    virtual ~TagBase() = default;
};

bool operator== (const std::unique_ptr<TagBase>& lhs, const TagBase& base) {
    return base == *(lhs.get());
}

class Namespace {
public:
    template<typename T>
    void AddTag() {
        tags.push_back(std::unique_ptr<T>(new T()));
    }
    
    bool HasTag(const TagBase& tag) const {
        return std::find(std::begin(tags), std::end(tags), tag) != tags.end();
    }

private:
    // using of std::unique_ptr is the only way?
    std::list<std::unique_ptr<TagBase>> tags;
};

struct TagA: public TagBase {};
struct TagB: public TagBase {};
struct TagC: public TagBase {};

int main (int argc, const char* argv[])
{
    Namespace nm;
    
    nm.AddTag<TagA>();
    nm.AddTag<TagB>();
    
    assert(nm.HasTag(TagA{}));
    assert(nm.HasTag(TagB{}));
    assert(nm.HasTag(TagC{}) == false);
}

It works, but I have some thoughts about:

  1. Using "typeid" in this context is appropriate? Is this portable?
  2. I can't use raw TagBase class in "std:list" because of slicing. Can I avoid to use "unique_ptr" because of heap allocation?
  3. Maybe there is a better pattern to do this?

I also tried to do this with intense use of templates(std::is_same ... etc) but haven't got proper solution.

Aucun commentaire:

Enregistrer un commentaire