2010-02-22 1 views
1

У меня слишком много информации для работы с, поэтому пока я отвечу на этот вопрос, пока не смогу разобраться во всем и решить окончательную реализацию! Спасибо тонну gf и Simon Buchan. Хотелось бы, чтобы я согласился с вашими ответами, так как они оба являются определенными возможностями!Выполнение этой задачи на C++; Миграция из AS3.0

Дополнительно/переработан Концептуальная информация по предложению:

Что я стремлюсь сделать;

Я делаю игру. В этой игре каждый используемый объект является экземпляром класса DOBJ. Класс TUR расширяет класс DOBJ. Класс SHO расширяет класс TUR.

Каждый класс TUR имеет массив SHO, хранящихся в массиве SHOARR. Каждому экземпляру SHO должен быть предоставлен набор инструкций.

Я знаю, что мог бы сделать 1000 различных классов SHO, которые имеют свои инструкции, установленные во время строительства.

Однако, учитывая, что у меня будет так много разных действующих экземпляров SHO, меня интересовал другой способ передачи набора инструкций. Благодаря конструкции SHO был бы идеалом.

Инструкции, которые я пытаюсь передать каждому SHO, являются простыми, если заявления;

if(frame > 64) { rotation += 4; };
if(state == 0 && frame < 32) { xs = 12; ys = 12; state = 1; };

Оригинальный вопрос

Миграция из ActionScript3.0 на C++, оказывается испытание на самом деле. Спасибо тем, кто ответил на мои вопросы до сих пор, а также тем, кто открыл стек переполнения в первую очередь. Onto вопрос ... (TL; DR вблизи дна, чтобы получить прямо на вопрос)

Я пытающегося применить ту же логику, что я мог бы применить в AS3.0 к моему проекту в C++ и это просто не очень хорошо.

В AS3.0 Я привык к удалению любого типа данных в массив. Это сделало вещи довольно простыми. Теперь, когда я столкнулся с разработчиком C++, я понял, что больше не могу этого сделать.

Так что теперь я застрял в этой проблеме переписывания маленькой системы искусственного интеллекта на новом языке, где место для движения системы даже не совместимо!

Вот пример фрагмента кода, который я писал в AS3.0;

AI[NUM][1](OBJ, AI[NUM][2], AI[NUM][3]);

А.И. быть массивом, NUM представляет собой целое число, OBJ, являющийся экземпляром класса.

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

В этом случае;

AI[NUM][1] будет функция AI[NUM][2] будет переменная AI[NUM][3] бы номер

Вообще, мой ИИ был запущен на вызов функции, чтобы изменить или сравнить переменную с номером.

Примером может быть;

CompareST(someObject, "x", 500);

и возвращает истину, если переменная х SomeObject была меньше (ST) 500.

ИИ массив сам был просто заполнен с массивами вызовов, подобных этим.

Совершенно новый для C++ Я понятия не имел, как это сделать, поэтому я немного искал и читал множество разных сайтов и пришел к выводу, что я должен искать указатели на функции.

Однако, прочитав немного в них, я пришел к выводу, что это не поможет мне реализовать мою цель. Хотя это помогло мне вызвать такие функции, как я хотел их назвать, это не помогает мне складывать разные типы данных в один массив массивов.

TL; DR

EDIT ++:

Что мне нужно для каждого объекта представляет собой набор инструкций для проверки каждого кадра. Однако для каждого экземпляра класса инструкции должны быть разными.

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

Таким образом, мне понадобился способ передать набор инструкций каждому из них через его конструктор и прочитать + выполнить их в любое время, когда вызывается их функция think().

Моей конечной целью (кроме того, чтобы узнать об этом лучше), было бы иметь множество вызовов функций, например;

A[n][0](O, A[n][1], A[n][2]);

Где; O является экземпляром, который функция изменяет A[n][0] является функцией (равенство или сравнение) A[n][1] - это переменная, например; «x», O["x"] (или указатель на эту переменную в случае C++) A[n][2] - это значение для изменения переменной или сравнения ее с.

И я не уверен, как бы переписать это на C++ или изменить его для работы по-другому.

Aftermath/Дополнительная информация

То, что я на самом деле стремится сделать, это быть в состоянии дать объект, набор инструкций, в момент его создания, с помощью конструктора. Например, при создании дать объектной инструкции подождать 64 кадра, а затем повернуть в противоположном направлении, было бы что-то вроде этого;

t.AI = [ [ 1, AIF.CompareET, "STATE", 0, AIF.CompareGT, "FRAME", 64, 0, AIF.EqualityAT, "baseRotation", 180, AIF.EqualityET, "STATE", 1 ] ];

В псевдокоде;

(1 в массиве обозначает, как читать остальную часть массива, в этом случае все до нечетного 0 [тот, который приходит после 64], является сравнением. Если кто-либо из них не работает, что-либо после 0 не будет рассматриваться)

Сравнить STATE равно (ET) 0, если верно
Сравнить КАДР является greather чем (GT) 64, если это правда
Добавить 180 в (AT) baseRotation, установленное состояние, равное 1

Извините, что так получилось очень долго. Надеюсь, это понятно, и я не прошу что-то глупость объяснить.

+2

Я думаю, вам будет лучше, если вы сосредоточитесь на своих требованиях к этой функциональности (т. Е. Концептуально). Попытка напрямую перевести это, скорее всего, плохая идея. –

+0

Я знаю, чего я пытаюсь выполнить, я просто не знаю, как это сделать на C++; поэтому я попытался использовать AS3.0, чтобы выразить свои потребности так, как я уже знал. Что мне нужно для каждого объекта - это набор инструкций для каждого кадра. Однако для каждого экземпляра класса инструкции должны быть разными. Я планирую иметь много разных экземпляров, поэтому создание класса для каждого из них необоснованно. Таким образом, мне нужен был способ передать набор инструкций каждому из них через его конструктор и прочитать + выполнить их в любое время, когда вызывается их функция think(). – user247334

+0

Является ли производительность важной проблемой? Вы передаете только интегральные значения в списке инструкций? Что мешает вам использовать функциональные возможности, используя наследование? –

ответ

1

Вы можете хранить функции, используя указатели на функции или функторы. Типы типов, хотя и не поддерживаются на C++, вы должны использовать там собственные решения.

Одним из возможных вариантов было бы использовать Boost.Any (или лучше, Boost.Variant, если используется только фиксированный набор типов):

typedef void (*Function)(Object*, const std::string&, boost::any&); 
std::vector<Function> functions; 

Учитывая некоторые функции:

void f(Object* obj, const std::string& name, boost::any& value) { 
    // ... 
} 

можно хранить и вызов это похоже на ваш пример:

functions.push_back(&f); 
functions[0](obj, "x", boost::any(500)); 

Чтобы использовать декларативный синтаксис, есть три варианта, которые приходят на ум:

  • вы используете подобный подход и имеют центральный «интерпретатор» функцию, например, на основе коммутатора (не забудьте переключиться на целые числа или указатели на элементы вместо строк, если вам нужна производительность)
  • вы изобретаете свой собственный язык и генерируете код C++ из файлов описания
  • Вы создаете функциональные объекты в декларативный способ

Для композиции, вы можете использовать Boost.Bind или что-то вроде пользовательских объектов, которые представляют собой операции:

struct Operation { 
    virtual ~Operation() {} 
    virtual bool operator()(Object&) = 0; 
};  

template<class T> 
struct GreaterThen : Operation { 
    typedef T Object::*Member; 
    Member member; 
    const T value; 
    CompareGT(Member member, const T& value) : member(member), value(value) {} 
    bool operator()(Object& obj) { return (obj.*member > value); } 
}; 

template<class T> 
struct SetTo : Operation { 
    typedef T Object::*member; 
    Member member; 
    const T value; 
    SetTo(Member member, const T& value) : member(member), value(value) {} 
    bool operator()(Object& obj) { obj.*member = value; return true; } 
}; 

Теперь мы можем построить список операций:

typedef std::vector<Operation*> OpList; 
OpList operation; 
operations.push_back(new GreaterThen<int>(&Object::Frame, 64)); 
operations.push_back(new SetTo<int>(&Object::State, 1)); 

Мы можем использовать вспомогательные функции, чтобы избежать необходимости указывать типы шаблонов:

template<class T> 
Operation* opGreaterThen(T Object::*mem, const T& val) { 
    return new GreaterThen<T>(mem, val); 
} 

Если предположить подобный помощник для SetTo и используя Boost.Assign выше становится:

OpList operations = boost::assign::list_of 
    (opGreaterThen(&Object::Frame, 64)) 
    (opSetTo  (&Object::State, 1)); 

Выполнение операций становится следующим:

OpList::iterator it = operation.begin(); 
for(; it != operations.end(); ++it) { 
    Operation& op = *it; // just for readability 
    if(!op(someObject)) break; // stop if operation returns false   
} 
+0

Я добавил дополнительную информацию к началу моего сообщения, описывая именно контекст и то, к чему я стремился. Пожалуйста, дайте мне знать, если вы чувствуете, что мне еще нужно добавить дополнительную информацию. Для объектов функции, позволил бы мне легко настроить множество различных типов экземпляров SHO? В конце концов, если я не могу этого избежать, у меня будет очень большая функция с наборами инструкций в зависимости от целочисленного ввода и просто установите каждый экземпляр SHO для запуска на основе заранее написанного набора инструкций. Я буду читать объекты Boost и функции. – user247334

1

Ничего себе.

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

Если это так, вы ищете указатели на функции. Попробуйте this tutorial.

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

int (TMyClass::*functptr)(classname, int, int) = NULL;    // C++ 

Затем назначьте его позже:

this.functptr = &TMyClass::doitthisway; 
+0

Я попытался использовать указатели на функции, но обнаружил, что проблема возникает, когда мне приходится вызывать функцию, чтобы на самом деле пойти и вызвать эти функции. Изначально, каждый объект вызывал функцию мысли, и в этой функции он считывал все вызовы функций в своем массиве ИИ и делал что-то по-другому. В противном случае я не мог себе представить способ его настройки, поэтому я могу настроить его как расширенный оператор if ... if (this() вызов функции возвращает true и этот() один тоже) {сделайте это ; это; и это; } Мне нужно несколько из тех, которые нужно проверить и установить в конструкции. – user247334

1

Хотя возможно (хотя и боль) иметь массив произвольных типов, вам в значительной степени он никогда не понадобится, так как вы должны знать что-то о том, где можно сделать что-нибудь интересное: например, ваш пример, «TL DR», кажется, выглядеть так:

struct AIRule { 
    // Can only handle comparing ints, see later for more general solution. 
    typedef bool compare_type(AIObject*, AIObject::*int, int); 
    compare_type* compare; 
    AIObject* object; 
    AIObject::int* member; 
    int comparand; 
}; 

Так что теперь вы можете сделать что-то вроде:

bool ai_equal(AIObject* object, AIObject::int* member, int comparand) { 
    return object->*member == comparand; 
} 

... 
    ai[n].compare = &ai_equal; 
    ai[n].object = some_object; 
    ai[n].member = &AIObject::some_member; 
    ai[n].comparand = 50; 
... 
    if (ai[n].compare(ai[n].object, ai[n].member, ai[n].comparand)) { 
     ... 
    } 

Это просто перемещает проблему любого типа из массива правил для member хотя. C++ должен знать, по крайней мере, сколько байтов является членом, а строка (например) может быть намного больше, чем int. Вы можете обойти это, используя указатели: по существу, это версия C++, но вам нужно удалить ее самостоятельно (или вы будете утечка памяти!), После чего метод интерфейса ниже упрощается.

Если бы я делать то, что вы, кажется, хотите, я хотел бы использовать наследование:

struct Sprite { 
    int frame; 
    double rotation; 

    Sprite() { 
     frame = 0; 
     rotation = 0.0; 
    } 
    virtual ~Sprite() {} 

    virtual void think() { 
     ++frame; 
    } 

    virtual void draw() { 
     ... 
    } 
}; 

struct RotatingSprite : public Sprite { 
    int state; 

    MyShape() { 
     state = 0; 
    } 

    void think() { 
     Sprite::think(); 
     if (state == 0 && frame > 64) { 
      state = 1; 
      rotation += 180.0; 
     } 
    } 
}; 

Или указатель функции:

struct Sprite { 
    int frame; 
    double rotation; 
    void (*think)(Sprite*); 

    Sprite() { 
     frame = 0; 
     rotation = 0.0; 
    } 
}; 

void rotate_think(Sprite* sprite) { 
    if (sprite->state == 0 && sprite->frame > 64) { 
     sprite->state = 1; 
     sprite->rotation += 180.0; 
    } 
} 

... 
    sprite->think = &rotate_think; 

Если вам действительно нужно сделать это динамически, я бы рекомендовал использовать ++ часть C++. Для предикатов (предикат как раз то, что возвращает логическое значение, как isLowerCase()) создать интерфейс AIPredicate, а действия интерфейс AIAction:

struct AIPredicate { 
    // "When you delete an AIPredicate, delete the full type, not just this interface." 
    virtual ~AIPredicate() {} 
    // "You can treat this as a function (operator()) but I'm not providing an implementation here (= 0)" 
    virtual bool operator()(AIObject* object) = 0; 
}; 

struct AIAction { 
    virtual ~AIAction() {} 
    virtual void operator()(AIObject* object) = 0; 
}; 

struct AIRule { 
    // std::auto_ptr (or std::unique_ptr if you can use C++0x) will delete predicate for you. 
    // Add "#include <memory>" to your includes if it complains (most std headers will include it already) 
    std::auto_ptr<AIPredicate> predicate; 
    std::auto_ptr<AIAction> action; 
}; 

Теперь вы можете сделать типы, как:

struct AIFrame : public AIPredicate { 
    // Implement the operator() member AICondition promises. 
    bool operator()(AIObject* object) { 
     return object->foo < 100; 
    } 
}; 

... 
    // Use .reset() instead of = if you use std::unique_ptr. 
    ai[n].predicate = new AIFooIsLow(); 

Если вы хотите иметь очень общий тип предиката, вы можете использовать очень мощные (и сложные) шаблоны особенность:

// The naming convention I'm using here is 'T'TitleCase for template parameters, TitleCase for types, 
// lower_case for arguments and variables and '_'lower_case for members. 
template<typename TMemberType, AIObject::TMemberType* TMember> 
struct AIMemberEquals : public AIPredicate { 
    // Constructor: Initializes a new instance after it is created. 
    AIMemberEquals(TMemberType comparand) { 
     // Save comparand argument so we can use it in operator(). 
     _comparand = comparand; 
    } 

    bool operator()(AIObject* object) { 
     return object->*TMember == comparand; 
    } 
    // Stores the value to compare. 
    TMemberType _comparand; 
}; 

к сожалению, создание шаблонов выглядит немного сумасшедший:

ai[n].predicate = new AIMemberEquals<int, &AIObject::some_member>(100); 

Прочти, как «создать новый экземпляр (типа, который AIMemberEquals применяется к ИНТ и (член some_member из AIObject) создает) с аргументом 100».

Когда вы управление памятью несколько предикатов становятся немного более трудным без unique_ptr или shared_ptr C++ 0x в, типов, которые будут удалять объект для вас, так как станд :: auto_ptr не работает в контейнерах:

#include <vector> 

struct AIData { 
    // vector is fairly close to AS3's Array type, it is a good default for 
    // arrays of changing or unknown size. 
    std::vector<AIPredicate*> predicates; 

    // Destructor: will be run before the memory for this object is freed. 
    ~AIData() { 
     for (int i = 0; i != predicates.size(); ++i) { 
      delete predicates[i]; 
     } 
    } 
}; 

... 
    ai[n].predicates.push_back(new AIFooIsLow()); 
... 
    for (int i = 0; i != ai[n].predicates.size(); ++i) { 
     (*ai[n].predicates[i])(ai[n].object); 
    } 

в C++ 0x:

struct AIData { 
    // unique_ptr will delete it for you, so no ~AIData() needed. 
    std::vector<unique_ptr<AIPredicate>> predicates; 
}; 

Ваш последний пример может в C++ выглядеть примерно так:

std::auto_ptr<Shape> shape(new Shape()); 
... 
std::auto_ptr<AIRule> rule(new AIRule()); 

rule->predicates.push(new AIMemberEquals<int, &Shape::state>(0)); 
rule->predicates.push(new AIMemberGreater<int, &Shape::frame>(64)); 

rule->actions.push(new AIAddMember<double, &Shape::rotation>(180.0)); 
rule->actions.push(new AISetMember<int, &Shape::state>(1)); 

shape->ai.push(rule); // .push(std::move(rule)); if you are using unique_ptr 

Конечно, не так красиво, но он работает и довольно гибкий.

+0

Это, на первый взгляд, выглядело невероятно устрашающе. Тем не менее, чтение через это много вещей, казалось, имело смысл, хотя я никогда не использовал почти все это. В конце концов, я работаю только на этом языке около 2 недель. Это, в основном, новая земля. Огромная благодарность за столь активные усилия. – user247334

+0

Ну, это большой кусок самого сложного языка в мире, поэтому вы, вероятно, должны быть запуганы :). Дело в C++, хотя есть, по крайней мере, 7 разных способов сделать все, с разными компромиссами, поэтому просто иметь дело с тем, что имеет для вас смысл, и бояться «нового» :). –