2017-08-01 5 views
0

Je travaille sur un moteur de jeu en tant que projet pendant l'été. Chaque composant scriptable devrait avoir accès à certaines méthodes de la scène dans laquelle il se trouve. Pour que cela soit possible, je passe lambdas de la scène qui appelle les méthodes respectives au scriptable où elles sont implicitement converties en types std :: function.Donner un autre accès à des méthodes spécifiques

Scene.h:

class Scene 
{ 
private: 
    unsigned int _currentId; 
    std::vector<System*> _systems; 

    //SCRIPTABLE NEEDS THE BELOW METHODS THESE EXCLUSIVELY: 

    bool exists(unsigned id); 
    void destroy(unsigned int); 
    void addComponent(Component*, unsigned int); 
    template<typename T> T& getComponent(unsigned int); 
    template<typename T> bool hasComponent(unsigned int); 
    template<typename T> void removeComponent(unsigned int); 

protected: 
    unsigned int instantiate(std::vector<Component*>); 

public: 
    Scene(ChangeSceneCallback); 
    ~Scene(); 
    void initiate(); 
    void update(long dt); 
}; 

template<typename T> 
inline T & Scene::getComponent(unsigned int id) 
{ 
    for (System* system : _systems) { 
     if (system->corresponds(T)) { 
      return static_cast<T*>(system->getComponent(entityId)); 
     } 
    } 
} 

template<typename T> 
inline bool Scene::hasComponent(unsigned int id) 
{ 
    for (System* system : _systems) { 
     if (system->corresponds(T)) { 
      return system->contains(id); 
     } 
    } 
} 

template<typename T> 
inline void Scene::removeComponent(unsigned int id) 
{ 
    for (System* system : _systems) { 
     if (system->corresponds(T)) { 
      return system->destroy(id); 
     } 
    } 
} 

La méthode de rappel fonctionne pour les fonctions non-modèle i besoin d'un accès, mais pas ceux templated, il est donc hors de question.

scriptable:

typedef std::function<void(int)>         ChangeSceneCallback; 
typedef std::function<int(std::vector<Component*>)>     InstantiateCallback; 
typedef std::function<void(int)>         DestroyCallback; 
typedef std::function<bool(int)>         ExistCallback; 
typedef std::function<void(Component*, unsigned int)>    AddComponentCallback; 


class Scriptable: public Component 
{ 
protected: 
    ChangeSceneCallback changeScene; 
    InstantiateCallback instantiate; 
    DestroyCallback destroy; 
    ExistCallback exists; 
public: 
    ~Scriptable(); 
    Scriptable(); 
    void assignCallbacks(ChangeSceneCallback, InstantiateCallback etc ...); 

    virtual void init() = 0; 
    virtual void update() = 0; 
}; 

scriptable ne peut pas avoir accès aux méthodes publiques en scène, car cela donnerait l'accès utilisateur/développeur pour les (scriptable est une classe de base pour le comportement du jeu). C'est pourquoi je dois trouver quelque chose qui donne un accès limité à la scène.

Des pensées?

+0

Il n'y a pas de pointeur vers la fonction de modèle. Cela nécessiterait une compilation de code d'exécution. –

+0

La question n'a aucun sens. Tant que le paramètre du template n'est pas fixé (spécialisé), template est un template. Ce n'est pas un type, ce n'est pas une fonction. Vous pouvez définir un template typedef (en utilisant les fonctionnalités C++ 11), mais cela ne vous permettra pas de l'utiliser comme un type sans spécifier le paramètre template. – AnT

+1

Cela vous dérange-t-il d'expliquer ce que 'system-> correspond (T)' est supposé vouloir dire? –

Répondre

1

Vous ne pouvez pas avoir un "rappel de modèle" effacé. Vous devez choisir entre le modèle ou le type d'effacement. Laisse-moi expliquer.

Voici à quoi ressemble un "rappel de modèle". Ceci est en fait un lambda générique:

auto print_callback = [](auto var) { 
    std::cout << var << std::endl; 
} 

print_callback(4) ;  // prints "4" 
print_callback(4.5);  // prints "4.5" 
print_callback("hello"); // prints "hello" 

Il semble bon, mais notez que vous ne pouvez pas le faire avec std::function, puisque vous devez prédéfinir la signature.

std::function<void(int)> func_print_callback = print_callback; 

func_print_callback(5); // Yay! Prints "5" 
func_print_callback("hello"); // error 

La chose est, vous pourriez penser que la limitation est seulement parce std::function besoin d'une signature spécifique pour travailler avec, mais la limitation est beaucoup plus profond que cela.

La chose est, le est pas modèle fonction. Ils n'existent pas. Le modèle de fonction d'autre part, existent. Pourquoi je souligne tellement sur l'ordre de mes mots est parce que le nom de cette chose dit tout: ce n'est pas une fonction, c'est un modèle qui est utilisé pour faire des fonctions.

Voici un exemple simple:

template<typename T> 
void foo(T t) { 
    std::cout << t << std::endl; 
} 

Cette fonction n'est pas compilé. Parce que ce n'est pas une fonction. Aucune fonction foo n'existera tant que le trou T n'a pas été rempli.

Comment remplir le trou nommé T censé être un type?

En le remplissant d'un type de cours!

foo(5.4); // the hole T is `double` 

Lorsque le compilateur voit, il sait qu'il faut une fonction nommée foo qui prend un double en tant que paramètre. Il n'y a aucune fonction nommée foo qui prend un double. Mais nous avons donné au compilateur un outil pour en créer un: le template!

Ainsi, le compilateur génère cette fonction:

void foo_double(double t) { 
    std::cout << t std::endl; 
} 

Le mot est ici ceci: générer. Le compilateur doit créer la fonction pour exister. Le compilateur génère du code pour vous.

Lorsque la fonction est générée et compilée, T n'existe plus. Un paramètre template est une entité à la compilation, et seul le compilateur est au courant.


Maintenant, je vais vous expliquer pourquoi il n'y a pas une telle chose comme un rappel de modèle.

Les conteneurs de type effacés tels que std::function sont implémentés avec un pointeur sur la fonction. J'utiliserai des alias de type pour faciliter un peu la syntaxe. Il fonctionne comme ceci:

// A function 
void foo(int) {} 

// The type of the pointer to function 
using func_ptr = void(*)(int); 

// A pointer to foo 
func_ptr ptr = &foo; 

Le pointeur vers la fonction foo a une valeur qui pointe vers l'emplacement de foo dans la mémoire.

Imaginez maintenant que nous avons un moyen d'avoir un pointeur de fonction de modèle. Nous devrions pointer vers une fonction qui n'existe pas encore. Il n'a pas d'emplacement de mémoire, donc cela n'a aucun sens. Et à travers le pointeur, lorsqu'il est appelé en tant que fonction, vous devez générer le code de la fonction.

Puisqu'un pointeur vers la fonction peut pointer vers n'importe quelle fonction, même des fonctions qui ne sont pas encore connues du compilateur, vous devrez générer le code de fonction et le compiler. Mais la valeur du pointeur, vers laquelle pointe notre pointeur, est définie à l'exécution! Il faudrait donc compiler du code au moment de l'exécution, pour un code que vous ne connaissez pas encore, à partir d'une valeur qui n'existe pas, quand le compilateur n'existe plus. Comme vous pouvez le voir, le pointeur vers la fonction de modèle, le modèle std::function ou la fonction de modèle virtuel ne peuvent pas exister.


Maintenant que vous avez compris le problème, permettez-moi de proposer une solution: abandonner l'utilisation de rappel. Vous devriez appeler ces fonctions directement.

Vous semblez utiliser le rappel uniquement pour pouvoir appeler des fonctions membres privées. C'est la mauvaise façon de le faire, même si cela fonctionne. Ce dont vous avez besoin est friend, la fonctionnalité de C++ qui vous permet d'accéder aux membres privés.

class Scene { 
    friend Component; 

    // ... 
}; 

class Component { 
protected: 

    // Let `scene` be a reference to your scene 

    void addComponent(Component* c, unsigned int id) { 
     scene.addComponent(c, id); 
    } 

    template<typename T> 
    T& getComponent(unsigned int id) { 
     return scene.getComponent<T>(id); 
    } 

    template<typename T> 
    bool hasComponent(unsigned int id) { 
     return scene.hasComponent(id); 
    } 

    template<typename T> 
    void removeComponent(unsigned int id) { 
     removeComponent(id); 
    } 

    // ... 
}; 

Depuis la classe Component est le seul ami à Scene, il ne peut appeler des fonctions membres privées. Puisque toutes ces nouvelles fonctions définies dans Component sont protégées, seule la classe qui s'étend de Component peut les appeler. Ils sont invoqués comme suit:

class Scriptable : public Component { 
    void foo() { 
     hasComponent<Bar>(87); // works, call function defined in `Component` 
    } 
}; 
+0

Merci! C'était tout à fait la réponse, je pense que j'ai besoin de le lire à nouveau haha. –

+1

@ojojkolol Je vous suggère d'en savoir plus sur les modèles et leur fonctionnement. Ils sont l'un des plus grands sujets en C++ –

+0

Je le ferai, merci! Je vois que vous travaillez aussi sur un moteur de jeu, comment ça se passe? –