Контейнер OneOfAType — хранение того каждый данный тип в контейнере — является мной от основы здесь?

У меня есть интересная проблема, это неожиданно возникло в своего рода находящемся на передаче моем компиляторе. Каждая передача не знает ничего из других передач, и общий объект передается цепочка, когда это идет, после шаблона цепи инстанций.

Объект, который проводится, является ссылкой на файл.

Теперь, во время одного из этапов, можно было бы хотеть связать большой блок данных, таких как хеш того файла SHA512, который требует, чтобы разумное количество времени вычислило. Однако, так как тот блок данных используется только в том конкретном случае, я не хочу, чтобы все ссылки на файл должны были зарезервировать пространство для этого SHA512. Однако я также не хочу, чтобы другие передачи должны были повторно вычислить, SHA512 долго обсуждают и снова. Например, кто-то мог бы только принять файлы, которые соответствуют данному списку SHA512s, но они не хотят то значение, распечатанное, когда ссылка на файл добирается в конец цепочки, или возможно они хотят обоих, или... .etc.

То, в чем я нуждаюсь, является своего рода контейнером, которые содержат только один из данного типа. Если контейнер не содержит тот тип, он должен создать экземпляр того типа и сохранить его так или иначе. Это - в основном словарь с типом, являющимся вещью, используемой для поиска вещей.

Вот то, что я получил до сих пор, при этом соответствующий бит был FileData::Get<t> метод:

class FileData;
// Cache entry interface
struct FileDataCacheEntry
{
    virtual void Initalize(FileData&)
    {
    }
    virtual ~FileDataCacheEntry()
    {
    }
};

// Cache itself
class FileData
{
    struct Entry
    {
        std::size_t identifier;
        FileDataCacheEntry * data;
        Entry(FileDataCacheEntry *dataToStore, std::size_t id)
            : data(dataToStore), identifier(id)
        {
        }
        std::size_t GetIdentifier() const
        {
            return identifier;
        }
        void DeleteData()
        {
            delete data;
        }
    };
    WindowsApi::ReferenceCounter refCount;
    std::wstring fileName_;
    std::vector<Entry> cache;
public:
    FileData(const std::wstring& fileName) : fileName_(fileName)
    {
    }
    ~FileData()
    {
        if (refCount.IsLastObject())
            for_each(cache.begin(), cache.end(), std::mem_fun_ref(&Entry::DeleteData));
    }
    const std::wstring& GetFileName() const
    {
        return fileName_;
    }

    //RELEVANT METHOD HERE
    template<typename T>
    T& Get()
    {
        std::vector<Entry>::iterator foundItem = 
            std::find_if(cache.begin(), cache.end(), boost::bind(
            std::equal_to<std::size_t>(), boost::bind(&Entry::GetIdentifier, _1), T::TypeId));
        if (foundItem == cache.end())
        {
            std::auto_ptr<T> newCacheEntry(new T);
            Entry toInsert(newCacheEntry.get(), T::TypeId);
            cache.push_back(toInsert);
            newCacheEntry.release();
            T& result = *static_cast<T*>(cache.back().data);
            result.Initalize(*this);
            return result;
        }
        else
        {
            return *static_cast<T*>(foundItem->data);
        }
    }
};

// Example item you'd put in cache
class FileBasicData : public FileDataCacheEntry
{
    DWORD    dwFileAttributes;
    FILETIME ftCreationTime;
    FILETIME ftLastAccessTime;
    FILETIME ftLastWriteTime;
    unsigned __int64 size;
public:
    enum
    {
        TypeId = 42
    }
    virtual void Initialize(FileData& input)
    {
        // Get file attributes and friends...
    }
    DWORD GetAttributes() const;
    bool IsArchive() const;
    bool IsCompressed() const;
    bool IsDevice() const;
    // More methods here
};

int main()
{
    // Example use
    FileData fd;
    FileBasicData& data = fd.Get<FileBasicData>();
    // etc
}

По некоторым причинам, хотя, этот дизайн чувствует неправильно мне, а именно, потому что он делает целый набор вещей с невведенными указателями. Я сильно от основы здесь? Там существуют ранее библиотеки (повышение или иначе), который сделал бы это более ясным/легче понять?

5
задан Billy ONeal 11 July 2010 в 01:31
поделиться

2 ответа

Как уже говорилось в ergosys, std :: map - очевидное решение вашей проблемы. Но я вижу, что вы обеспокоены RTTI (и связанным с этим раздуванием). Фактически, для работы контейнера значений «любое» не требуется RTTI. Достаточно обеспечить соответствие между типом и уникальным идентификатором. Вот простой класс, который обеспечивает это отображение:

#include <stdexcept>
#include <boost/shared_ptr.hpp>
class typeinfo
{
    private:
        typeinfo(const typeinfo&); 
        void operator = (const typeinfo&);
    protected:
        typeinfo(){}
    public:
        bool operator != (const typeinfo &o) const { return this != &o; }
        bool operator == (const typeinfo &o) const { return this == &o; }
        template<class T>
        static const typeinfo & get()
        {
            static struct _ti : public typeinfo {} _inst;
            return _inst;
        }
};

typeinfo :: get () возвращает ссылку на простой синглтон без сохранения состояния, который позволяет проводить сравнения.

Этот синглтон создается только для типов T, где typeinfo :: get () выдается в любом месте программы.

Теперь мы используем это для реализации верхнего типа, который мы называем значением . value является держателем для value_box , который фактически содержит данные:

class value_box
{
    public:
        // returns the typeinfo of the most derived object
        virtual const typeinfo& type() const =0;
        virtual ~value_box(){}
};

template<class T>
class value_box_impl : public value_box
{
    private:
        friend class value;
        T m_val; 
        value_box_impl(const T &t) : m_val(t) {}
        virtual const typeinfo& type() const
        {
            return typeinfo::get< T >();
        }
};
// specialization for void.
template<>
class value_box_impl<void> : public value_box
{
    private:
        friend class value_box;
        virtual const typeinfo& type() const
        {
            return typeinfo::get< void >();
        }
    // This is an optimization to avoid heap pressure for the 
    // allocation of stateless value_box_impl<void> instances:
    void* operator new(size_t) 
    {
        static value_box_impl<void> inst;
        return &inst;
    }
    void operator delete(void* d) 
    {
    }

};

Вот исключение bad_value_cast:

class bad_value_cast : public std::runtime_error
{
    public:
        bad_value_cast(const char *w="") : std::runtime_error(w) {}
};

И вот значение:

class value
{
    private:
        boost::shared_ptr<value_box> m_value_box;       
    public:
        // a default value contains 'void'
        value() : m_value_box( new value_box_impl<void>() ) {}          
            // embedd an object of type T.
        template<class T> 
        value(const T &t) : m_value_box( new value_box_impl<T>(t) ) {}
        // get the typeinfo of the embedded object
        const typeinfo & type() const {  return m_value_box->type(); }
        // convenience type to simplify overloading on return values
        template<class T> struct arg{};
        template<class T>
        T convert(arg<T>) const
        {
            if (type() != typeinfo::get<T>())
                throw bad_value_cast(); 
            // this is safe now
            value_box_impl<T> *impl=
                      static_cast<value_box_impl<T>*>(m_value_box.get());
            return impl->m_val;
        }
        void convert(arg<void>) const
        {
            if (type() != typeinfo::get<void>())
                throw bad_value_cast(); 
        }
};

Удобный синтаксис преобразования:

template<class T>
T value_cast(const value &v) 
{
    return v.convert(value::arg<T>());
}

И это все. Вот как это выглядит:

#include <string>
#include <map>
#include <iostream>
int main()
{
    std::map<std::string,value> v;
    v["zero"]=0;
    v["pi"]=3.14159;
    v["password"]=std::string("swordfish");
    std::cout << value_cast<int>(v["zero"]) << std::endl;
    std::cout << value_cast<double>(v["pi"]) << std::endl;
    std::cout << value_cast<std::string>(v["password"]) << std::endl;   
}

Хорошая вещь в том, что у вас есть собственная реализация any , заключается в том, что вы можете очень легко адаптировать ее к нужным вам функциям, что довольно утомительно с boost :: любой. Например, есть несколько требований к типам, которые может хранить значение: они должны быть копируемыми и иметь открытый деструктор. Что делать, если все используемые вами типы имеют оператор << (ostream &, T) и вам нужен способ распечатать словари? Просто добавьте метод to_stream в box и перегрузите оператор << для значения, и вы можете написать:

std::cout << v["zero"] << std::endl;
std::cout << v["pi"] << std::endl;
std::cout << v["password"] << std::endl;

Вот pastebin с указанным выше, он должен скомпилироваться из коробки с g ++ / boost: http: // pastebin.com / v0nJwVLW

РЕДАКТИРОВАТЬ : Добавлена ​​оптимизация, чтобы избежать выделения box_impl из кучи: http://pastebin.com/pqA5JXhA

9
ответ дан 13 December 2019 в 22:01
поделиться

Вы можете создать хэш или карту строки в boost::any. Строковый ключ может быть извлечен из any::type().

1
ответ дан 13 December 2019 в 22:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: