HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Type to instance map

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
typeinstancemap

Problem

I needed a class where I could store an instance of any type. The goal was an interface as follows:

class TypeMap {
    template
    T& get();
};


Such that:

  • Iff &m1 == &m2 then &m1.get() == &m2.get().



  • The first call to m.get for all m, T combinations returns a reference to a default-constructed T.



  • A TypeMap going out of scope deallocates all memory that was accessible through it.



Here is my solution:

```
#pragma once

// Note to CR.SO: Provides the ASSERT macro.
#include "assert.hpp"
#include
#include

//! A map of types to values.
//!
//! Associative container which allows mapping of types to values of that
//! type.
class TypeMap : boost::noncopyable {
template
class TypeMapDetail {
struct Node : boost::noncopyable {
Node(void key, Node parent) : key(key), lhs(), rhs(), parent(parent), data() {}
void* key;
Node* lhs;
Node* rhs;
Node* parent;
T data;
};
Node* root;

typedef void (destruct_func)(void);
static void destroy_impl(void* p) {
delete static_cast(p);
}
destruct_func destroy;

// No member data past this point.

Node& get_parent_to_this(Node current) {
if (!current->parent)
return root;
if (current->parent->lhs == current)
return current->parent->lhs;
else
return current->parent->rhs;
}

void remove_specific(Node* current) {
Node*& parent_to_this = get_parent_to_this(current);
if (!current->lhs && !current->rhs) {
parent_to_this = nullptr;
return;
}
if (!current->lhs) {
parent_to_this = current->rhs;
current->rhs->parent = current->parent;
return;
}
if (!current->rhs) {
parent_to_this = c

Solution

I don't see why you use void as a key, when that pointer is always a TypeMap.

You could use a std::map as TypeMapDetail's member. (Yes, std::less is a safe total ordering, even though operator> is even easier.

I think I would approach the cleanup more like:

class TypeMap : boost::noncopyable {
    typedef void cleanup_func(TypeMap*);
    template
    class TypeMapDetail : boost::noncopyable {
        static std::map > objects;
    public:
        static T& get(TypeMap*);
        static void cleanup(TypeMap* tm) { objects.erase(tm); }
    };

    typedef std::map cleanup_map_type;
    cleanup_map_type cleanup_actions;

public:
    TypeMap() : cleanup_actions() {}
    ~TypeMap() {
        for (cleanup_map_type::iterator iter = cleanup_actions.begin();
             iter != cleanup_actions.end();
             ++iter) {
            iter->second(this);
        }
    }
    template 
    T& get() {
        cleanup_actions.insert(std::make_pair(
            std::cref(typeid(T)), &TypeMapDetail::cleanup));
        return TypeMapDetail::get(this);
    }
};


... which doesn't involve any of that static_cast undefined behavior at all. If C++11 is out, struct type_index is fairly easy to implement.

Code Snippets

class TypeMap : boost::noncopyable {
    typedef void cleanup_func(TypeMap*);
    template<typename T>
    class TypeMapDetail : boost::noncopyable {
        static std::map<TypeMap*, std::unique_ptr<T> > objects;
    public:
        static T& get(TypeMap*);
        static void cleanup(TypeMap* tm) { objects.erase(tm); }
    };

    typedef std::map<std::type_index, cleanup_func> cleanup_map_type;
    cleanup_map_type cleanup_actions;

public:
    TypeMap() : cleanup_actions() {}
    ~TypeMap() {
        for (cleanup_map_type::iterator iter = cleanup_actions.begin();
             iter != cleanup_actions.end();
             ++iter) {
            iter->second(this);
        }
    }
    template <typename T>
    T& get() {
        cleanup_actions.insert(std::make_pair(
            std::cref(typeid(T)), &TypeMapDetail<T>::cleanup));
        return TypeMapDetail<T>::get(this);
    }
};

Context

StackExchange Code Review Q#15316, answer score: 5

Revisions (0)

No revisions yet.