Общие типы с C++ и lua

У меня есть класс Entity, который содержит некоторые атрибуты (фрагменты данных). Эти атрибуты хранятся в карте из имени => значение.

class Entity
{
public:
    // Sets the attribute with the specified name.
    void attribute(const std::string& name, const GenericType& value) {
        m_attributes[name] = value;
    }

    // Returns the attribute with the specified name.
    GenericType& attribute(const std::string& name) {
        return m_attributes[name];
    }

    template<typename AttributeType>
    void attribute(const std::string& name, const AttributeType& value) {
        m_attributes[name] = GenericType(value);
    }

    template<typename AttributeType>
    AttributeType& attribute(const std::string& name) {
        return generic_cast<AttributeType>(m_attributes[name]);
    }

private:
    // Map of attributes from name => value.
    std::unordered_map<std::string, GenericType> m_attributes;
}

У меня есть метод для создания или перезаписи атрибута и еще один метод, который возвращает атрибут с указанным именем. Первые два метода доступны для Lua. Последние два метода предназначены для доступа и изменения атрибутов из исходного кода C++. Проблема в том, что атрибуты могут быть любого типа. Например, я хочу иметь возможность:

entity.attribute("Health", 100)
entity.attribute("Position", Vector3(1,2,3))
entity.attribute("Position").x = 4

Должна быть возможность читать и изменять атрибуты как из исходных файлов C++, так и из сценариев lua. Раньше я использовал ChaiScript, где использовал Boxed_Value класс как мой GenericType. Это работало хорошо, но чрезмерное время компиляции заставило меня искать в другом месте.

Есть ли способ добиться этого с помощью LuaBind (или любой другой библиотеки привязки Lua)? Класс luabind::object выглядит многообещающе, но он принимает указатель lua_State в своем конструкторе. Это беспокоит меня, так как я чувствую, что класс Entity действительно не должен ничего знать о состоянии Lua.


person Homar    schedule 29.11.2014    source источник
comment
Было бы полезно, если бы вы попросили поддержки со временем компиляции ChaiScript, есть несколько способов уменьшить его.   -  person lefticus    schedule 21.12.2014
comment
@lefticus Мне удалось сократить время компиляции, и я все еще использую ChaiScript, так как мне очень нравится этот язык. На самом деле вопрос не был задан исключительно из-за времени компиляции, которое я получал с ChaiScript. Меня также интересовало, как другие библиотеки связывания языков подходят к универсальным типам.   -  person Homar    schedule 21.12.2014
comment
очень хорошо, спасибо за обновление.   -  person lefticus    schedule 22.12.2014


Ответы (1)


В LuaBind вашему классу сущностей не нужно ничего знать о состоянии lua. Привязка Lua не обязательно должна быть синтаксически идентична C++ API 1-к-1, так как это совершенно разные языки.

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

блефы — это пример, демонстрирующий, что я имею в виду.

Простой пример: с LuaBridge:

несколько сложно создать простую, не скрученную вручную обвязку:

class Entity {
    std::map<std::string, int> attributes;

public:
    void attribute(std::string const& key,int value) {
        attributes[key] = value;
    }

    int& attribute(std::string const& key) {
        return attributes[key];
    }
};

вместо этого может быть привязана обертка:

class EntityWrapper {
    Entity entity;
public:
    void set_int(std::string const& key,int value) {
        entity.attribute(key,value);
    }


    int get_int(std::string const& key) {
        return entity.attribute(key);
    }
};

простая привязка:

void luabridge_bind(lua_State *L) {
 luabridge::getGlobalNamespace(L)
 .beginClass<EntityWrapper>("Entity")
 .addConstructor<void(*)(), RefCountedPtr<EntityWrapper> /* creation policy */ >()
    .addFunction("get_int", &EntityWrapper::get_int)
    .addFunction("set_int", &EntityWrapper::set_int)
 .endClass()
 ; 
}

и в Луа:

local e = Entity()
e:set_int("bla",42)
print(e:get_int("bla"))

Если вам нужно, чтобы Entity взаимодействовала с другими API, напишите небольшие оболочки, которые получают исходный обернутый объект и передают его другим функциям.

person Dmitry Ledentsov    schedule 29.11.2014
comment
Спасибо за подробный ответ. Моя проблема в том, что я не знаю конкретных типов атрибутов априори. Поэтому я не могу писать методы получения и установки для конкретных типов. - person Homar; 30.11.2014
comment
в этом случае будет довольно сложно иметь скриптовый API, если я не ошибаюсь. В какой-то момент вам придется перечислить все типы, используемые в привязке скрипта. Если это вариант, вы можете создать метапрограмму, которая не сможет скомпилироваться, если в API сценариев отсутствует преобразователь значений или привязка. Однако вам придется изменить свои общие методы, чтобы они стали частью вашей метапрограммы. то есть static_assert(has_binding<AttributeType>::value,...) или что-то еще должно быть достаточно - person Dmitry Ledentsov; 30.11.2014
comment
@Homar, поскольку вы используете шаблоны, разве вам не нужно знать, какие типы и атрибуты существуют во время компиляции? - person greatwolf; 30.11.2014
comment
Да, во время компиляции. Но я не хочу жестко кодировать методы получения и установки для определенных типов. Если я смогу заставить компилятор генерировать их, я думаю, что это будет хорошим решением. Мне нужно зарегистрировать классы с помощью LuaBind, поэтому мне нужны методы получения и установки для всех этих типов в дополнение к примитивным типам. - person Homar; 30.11.2014
comment
Кто-то должен знать, как преобразовать MySuperType в набор чисел, строк и логических значений, так что обойти это невозможно. Типичный компилятор C++ не имеет такого ИИ. Но вы можете помочь себе с помощью метапрограмм для создания преобразователей значений декларативным способом. Если вы можете перейти на управляемый код, преобразуйте свой код в управляемый код, и вы получите немного больше бесплатно. - person Dmitry Ledentsov; 30.11.2014
comment
@Homar, это поднимает еще один вопрос, например, что, если есть определенные типы / атрибуты, которые действительны, но в настоящее время не используются в стране C ++? Означает ли это, что он должен быть недоступен и в lua? Если вы явно не создадите это, компилятор не создаст его для вас. - person greatwolf; 30.11.2014