На каких платформах произойдет сбой, и как я могу его улучшить? - PullRequest
5 голосов
/ 17 сентября 2010

Я написал зачатки класса для создания динамических структур в C ++.Члены динамической структуры хранятся смежно (насколько показывают мои тесты) с тем же заполнением, которое компилятор вставил бы в эквивалентную статическую структуру.Таким образом, динамические структуры могут быть неявно преобразованы в статические структуры для взаимодействия с существующими API.

Прежде всего, я не верю себе, что могу написать код улучшенного качества, который может компилироваться и работать на более или менее любой платформе.,Какие части этого кода опасно нуждаются в модификации?

У меня есть еще один связанный с дизайном вопрос: является ли шаблонный метод доступа get единственным способом предоставления компилятору необходимой статической информации о типе для type-безопасный код?Как таковой, пользователь dynamic_struct должен указывать тип члена, к которому он обращается, всякий раз, когда он получает к нему доступ.Если этот тип изменится, все обращений станут недействительными и будут либо вызывать эффектные сбои, либо, что еще хуже, молчаливо проваливаться.И это не может быть поймано во время компиляции.Это огромный риск, и я хотел бы исправить его.

Пример использования:

struct Test {

    char a, b, c;
    int i;
    Foo object;

};

void bar(const Test&);

int main(int argc, char** argv) {

    dynamic_struct<std::string> ds(sizeof(Test));

    ds.append<char>("a") = 'A';
    ds.append<char>("b") = '2';
    ds.append<char>("c") = 'D';
    ds.append<int>("i") = 123;
    ds.append<Foo>("object");
    bar(ds);

}

И следующий код:

//
// dynamic_struct.h
//
// Much omitted for brevity.
//


/**
 * For any type, determines the alignment imposed by the compiler.
 */
template<class T>
class alignment_of {
private:

    struct alignment {

        char a;
        T b;

    }; // struct alignment

public:

    enum { value = sizeof(alignment) - sizeof(T) };

}; // class alignment_of


/**
 * A dynamically-created structure, whose fields are indexed by keys of
 * some type K, which can be substituted at runtime for any structure
 * with identical members and packing.
 */
template<class K>
class dynamic_struct {
public:


    // Default maximum structure size.
    static const int DEFAULT_SIZE = 32;


    /**
     * Create a structure with normal inter-element padding.
     */
    dynamic_struct(int size = DEFAULT_SIZE) : max(size) {

        data.reserve(max);

    } // dynamic_struct()


    /**
     * Copy structure from another structure with the same key type.
     */
    dynamic_struct(const dynamic_struct& structure) :
        members(structure.members), max(structure.max) {

        data.reserve(max);

        for (iterator i = members.begin(); i != members.end(); ++i)
            i->second.copy(&data[0] + i->second.offset,
                &structure.data[0] + i->second.offset);

    } // dynamic_struct()


    /**
     * Destroy all members of the structure.
     */
    ~dynamic_struct() {

        for (iterator i = members.begin(); i != members.end(); ++i)
            i->second.destroy(&data[0] + i->second.offset);

    } // ~dynamic_struct()


    /**
     * Get a value from the structure by its key.
     */
    template<class T>
    T& get(const K& key) {

        iterator i = members.find(key);

        if (i == members.end()) {

            std::ostringstream message;
            message << "Read of nonexistent member \"" << key << "\".";
            throw dynamic_struct_access_error(message.str());

        } // if

        return *reinterpret_cast<T*>(&data[0] + i->second.offset.offset);

    } // get()


    /**
     * Append a member to the structure.
     */
    template<class T>
    T& append(const K& key, int alignment = alignment_of<T>::value) {

        iterator i = members.find(key);

        if (i != members.end()) {

            std::ostringstream message;
            message << "Add of already existing member \"" << key << "\".";
            throw dynamic_struct_access_error(message.str());

        } // if

        const int modulus = data.size() % alignment;
        const int delta = modulus == 0 ? 0 : sizeof(T) - modulus;

        if (data.size() + delta + sizeof(T) > max) {

            std::ostringstream message;

            message << "Attempt to add " << delta + sizeof(T)
                << " bytes to struct, exceeding maximum size of "
                << max << ".";

            throw dynamic_struct_size_error(message.str());

        } // if

        data.resize(data.size() + delta + sizeof(T));

        new (static_cast<void*>(&data[0] + data.size() - sizeof(T))) T;

        std::pair<iterator, bool> j = members.insert
            ({key, member(data.size() - sizeof(T), destroy<T>, copy<T>)});

        if (j.second) {

            return *reinterpret_cast<T*>(&data[0] + j.first->second.offset);

        } else {

            std::ostringstream message;
            message << "Unable to add member \"" << key << "\".";
            throw dynamic_struct_access_error(message.str());

        } // if

    } // append()


    /**
     * Implicit checked conversion operator.
     */
    template<class T>
    operator T&() { return as<T>(); }


    /**
     * Convert from structure to real structure.
     */
    template<class T>
    T& as() {

        // This naturally fails more frequently if changed to "!=".
        if (sizeof(T) < data.size()) {

            std::ostringstream message;

            message << "Attempt to cast dynamic struct of size "
                << data.size() << " to type of size " << sizeof(T) << ".";

            throw dynamic_struct_size_error(message.str());

        } // if

        return *reinterpret_cast<T*>(&data[0]);

    } // as()


private:


    // Map from keys to member offsets.
    map_type members;

    // Data buffer.
    std::vector<unsigned char> data;

    // Maximum allowed size.
    const unsigned int max;


}; // class dynamic_struct

Ответы [ 3 ]

1 голос
/ 17 сентября 2010

Я думаю, что проверка типов может быть улучшена. Прямо сейчас он будет reinterpret_cast сам по себе любого типа с таким же размером.

Может быть, создать интерфейс для регистрации клиентских структур при запуске программы, чтобы они могли проверяться каждым участником - или даже перестраиваться на лету, или вначале создаваться более разумно.

#define REGISTER_DYNAMIC_STRUCT_CLIENT( STRUCT, MEMBER ) \
    do dynamic_struct::registry< STRUCT >() // one registry obj per client type \
        .add( # MEMBER, &STRUCT::MEMBER, offsetof( STRUCT, MEMBER ) ) while(0)
        //    ^ name as str ^ ptr to memb ^ check against dynamic offset
1 голос
/ 17 сентября 2010

В этом коде нет ничего плохого.Задержка проверки типов до тех пор, пока время выполнения не станет абсолютно допустимым, хотя вам придется приложить немало усилий, чтобы победить систему типов во время компиляции.Я написал класс однородного стека, в который можно было бы вставить любой тип, который функционировал бы аналогичным образом.

Однако вы должны спросить себя - для чего вы на самом деле собираетесь использовать это?Я написал однородный стек, чтобы заменить стек C ++ для интерпретируемого языка, что является довольно высоким порядком для любого конкретного класса.Если вы не делаете что-то радикальное, это, вероятно, неправильно.

Короче говоря, вы можете сделать это, и это не является незаконным, плохим или неопределенным, и вы можете заставить это работать - ноВы должны делать это только в том случае, если вам крайне необходимо делать что-то, выходящее за рамки обычного языка.Кроме того, ваш код ужасно умрет, когда C ++ 0x станет Standard, и теперь вам нужно переместиться и все остальное.

Самый простой способ думать о вашем коде - это фактически управляемая куча миниатюрного размера.,Вы помещаете в объекты различных типов ... они хранятся смежно и т. Д.

Редактировать: Подождите, вам не удалось обеспечить безопасность типов во время выполнения?Вы просто взорвали безопасность типов во время компиляции, но не заменили ее?Позвольте мне опубликовать какой-то намного более качественный код (возможно, он немного медленнее).

Редактировать: Ой, подождите.Вы хотите преобразовать вашу динамическую структуру в целом во произвольные неизвестные другие структуры во время выполнения?Ой.О чувак.О, серьезно.Какие.Просто нет.Просто не надо.Действительно, действительно, нет.Это так неправильно, это невероятно.Если бы у вас было отражение, вы могли бы сделать эту работу, но C ++ не предлагает этого.Вы можете обеспечить безопасность типов во время выполнения для каждого отдельного члена, используя dynamic_cast и стирание типов с наследованием.Не для всей структуры, поскольку, учитывая тип T, вы не можете сказать, что это за типы или двоичный формат.

0 голосов
/ 17 сентября 2010

У меня один вопрос: что из этого получается?

Я имею в виду, что это умный кусок кода, но:

  • вы возитесь с памятью, вероятность взрыва огромна
  • это тоже довольно сложно, я не все понял, и мне непременно пришлось бы позировать дольше ...

Что мне действительно интересно, так это то, что вы на самом деле хотите ...

Например, используя Boost.Fusion

struct a_key { typedef char type; };
struct object_key { typedef Foo type; };

typedef boost::fusion<
  std::pair<a_key, a_key::type>,
  std::pair<object_key, object_key::type>
> data_type;

int main(int argc, char* argv[])
{
  data_type data;
  boost::fusion::at_key<a_key>(data) = 'a'; // compile time checked
}

Используя Boost.Fusion, вы получаете отражение во время компиляции и правильную упаковку.

Я действительно не вижу необходимости выбора «среды выполнения» здесь (используя значение в качестве ключа вместо типа), когда вам все равно нужно передать правильный тип в присвоение (char против Foo).

Наконец, обратите внимание, что это можно автоматизировать благодаря программированию препроцессора:

DECLARE_ATTRIBUTES(
  mData,
  (char, a)
  (char, b)
  (char, c)
  (int, i)
  (Foo, object)
)

Не слишком многословно, чем типичное объявление, хотя a, b и т. Д. Будут внутренними типами, а не именами атрибутов.

Это имеет несколько преимуществ перед вашим решением:

  • проверка во время компиляции
  • полное соответствие с созданными по умолчанию конструкторами / конструкторами копирования / и т.д ...
  • гораздо более компактное представление
  • нет поиска во время выполнения "правильного" члена
...