Question

Je suis à peu près sûr que c'est possible, parce que je suis à peu près sûr de l'avoir vu faire. Je pense que c’est génial, mais j’accepterai volontiers des réponses du type & "C’est une idée terrible car ____ &";.

.

Disons que nous avons une structure de base.

struct vertex
{
    float x, y, z;
};

Maintenant, je veux implémenter des alias sur ces variables.

vertex pos;
vertex col;
vertex arr;

pos.x = 0.0f; pos.y = 0.5f; pos.z = 1.0f;
col.r = 0.0f; col.g = 0.5f; col.b = 1.0f;
arr[0] = 0.0f; arr[1] = 0.5f; arr[2] = 1.0f;

Idéalement, la troisième syntaxe ne pourrait être distinguée d’un tableau. Autrement dit, si j’envoyais arr en tant que paramètre de référence à une fonction qui attend un tableau de flottants dans lequel elle stockera des données (par exemple, de nombreuses fonctions OpenGL glGet), cela fonctionnerait correctement.

Qu'en penses-tu? Possible? Possible mais stupide?

Était-ce utile?

La solution

Ce que je ferais serait de créer des accesseurs:

struct Vertex {
    float& r() { return values[0]; }
    float& g() { return values[1]; }
    float& b() { return values[2]; }

    float& x() { return values[0]; }
    float& y() { return values[1]; }
    float& z() { return values[2]; }

    float  operator [] (unsigned i) const { return this->values_[i]; }
    float& operator [] (unsigned i)       { return this->values_[i]; }
    operator float*() const { return this->values_; }

private:
    float[3] values_;
}

Autres conseils

Les structures imbriquées sans nom dans une union ne sont pas du C ++ standard. Cela devrait cependant fonctionner:

struct Vertex
{
private:
   typedef float Vertex::* const vert[3];
   static const vert v;

public:
   typedef size_t size_type;
   float x, y, z;

   const float& operator[](size_type i) const {
      return this->*v[i];
   }

   float& operator[](size_type i) {
      return this->*v[i];
   }

};

const Vertex::vert Vertex::v = {&Vertex::x, &Vertex::y, &Vertex::z};

EDIT: Un peu plus d’informations. La structure utilise un tableau de 3 membres pointeurs vers données pour accéder aux données des opérateurs surchargés [].

La ligne & "; typedef float Vertex :: * const vert &"; signifie que vert est un pointeur sur un membre float de la structure Vertex. Le [3] signifie qu'il s'agit d'un tableau de 3 d'entre eux. Dans l'opérateur surchargé [], ce tableau est indexé et le membre pointeur-donnée est déréférencé et la valeur renvoyée.

De plus, cette méthode devrait fonctionner quels que soient les problèmes d'emballage: le compilateur est libre de remplir la structure de Vertex comme bon lui semble et cela fonctionnera toujours très bien. Une union anonyme rencontrera des problèmes si les flottants sont emballés différemment.

Utiliser un syndicat?

union vertex
{
    struct { float x, y, z; };
    struct { float r, g, b; };
    float arr[3];
};

Je ne le recommanderais pas - cela entraînerait de la confusion.

Ajouté :

Comme Adrian l'a indiqué dans sa réponse, cette union avec des membres de structure anonymes n'est pas prise en charge par ISO C ++. Cela fonctionne dans GNU G ++ (avec des plaintes sur le fait de ne pas être pris en charge lorsque vous activez '-Wall -ansi -pedantic'). Cela rappelle les jours C pré-pré-standard (pre-K & Et R 1st Edn), lorsque les noms des éléments de structure devaient être uniques dans toutes les structures et que vous pouviez utiliser des notations contractées pour obtenir un décalage dans la structure, et vous pouvez utiliser des noms de membre d'autres types de structure - une forme d'anarchie. Au moment où j'ai commencé à utiliser C (il y a longtemps, mais post-K & Et R1), c'était déjà un usage historique.

La notation affichée avec les membres d'union anonymes (pour les deux structures) est prise en charge par C11 (ISO / IEC 9899: 2011), mais pas par les versions antérieures de la norme C. La section 9.5 de la norme ISO / IEC 14882: 2011 (C ++ 11) contient des unions anonymes, mais GNU g++ (4.9.1) n'accepte pas le code affiché avec -pedantic, identifiant & Quot; warning: ISO C++ prohibits anonymous structs [-Wpedantic]. ".

Comme cette idée risque de semer la confusion, je ne suis pas particulièrement préoccupée par le fait qu’elle n’est pas standard; Je ne voudrais pas utiliser le mécanisme pour cette tâche (et je me méfierais d'utiliser des structures anonymes dans une union, même si cela était bénéfique).

Une préoccupation a été soulevée:

  

Les trois (x-y-z, r-g-b et le tableau) ne sont pas nécessairement alignés.

C’est une union à trois éléments; les trois éléments commencent à la même adresse. Les deux premiers sont des structures contenant 3 valeurs float. Il n'y a pas d'héritage ni de fonctions virtuelles permettant de donner différentes dispositions, etc. Les structures seront présentées avec les trois éléments contigus (en pratique, même si la norme autorise le remplissage). Le tableau commence également à la même adresse et sous réserve de l'absence de remplissage dans les structures, les éléments chevauchent les deux structures. Je ne vois vraiment pas qu'il y aurait un problème.

Vous pouvez obtenir cela avec un syndicat, comme d'autres l'ont mentionné. Surcharger la couleur et la position sur la même structure peut ne pas être une bonne idée (par exemple, ajouter deux couleurs signifie généralement que vous voulez saturer à 1,0, alors que l'ajout de vecteurs se fait de manière linéaire), mais superposer un flottant [] comme c’est parfait et un moyen bien accepté d’échanger des données avec GL / DirectX / etc.

Je vous recommande toutefois d'éviter de faire référence au même membre par différents alias dans la même étendue de fonctions, car cela vous mènera à un stand matériel désagréable appelé load-hit-store. En particulier, évitez ceci si vous pouvez:

vector foo; 
foo.x = 1.0f;
return foo[0] + foo[1];

Références?

template<typename T>
struct vertex {
    vertex() :
        r(data[0]), g(data[1]), b(data[2]),
        x(data[0]), y(data[1]), z(data[2])
    {
    }

    T *operator *() {
        return data;
    }

    const T *operator *() const {
        return data;
    }

    T data[3];
    T &r, &g, &b;
    T &x, &y, &z;
};

Je suppose que vous pouvez faire de la magie macro pour obtenir ce que vous voulez. Mais ça aura l'air moche. Pourquoi voulez-vous utiliser la même structure, sommet pour 3 types différents? Pourquoi ne pouvez-vous pas définir la classe pour la couleur? N'oubliez pas non plus que le sommet et la couleur ne sont pas identiques. Si vous changez quelque chose en sommet, cela affectera également la couleur si vous avez la même classe pour les deux.

Je ne suis pas sûr d'avoir bien compris la question. Mais il semble que vous deviez surcharger l'opérateur [] pour fournir un tableau comme un accès à votre struct / classe. Voir l'exemple mentionné ici: Surcharge des opérateurs

La structure suivante aura le comportement demandé:

struct vertex
{
private:
    float data[3];
public:
    float &x, &y, &z;
    float &r, &g, &b;

    vertex() : x(data[0]), y(data[1]), z(data[2]), r(data[0]), g(data[1]), b(data[2]) {
    }

    float& operator [](int i) { 
        return data[i];
    }
};

Mauvaise idée à mon avis, du moins pour l'exemple donné: l'inconvénient est que, quelle que soit la solution proposée, vous pourrez probablement attribuer librement & "; rgb &". ; instances vers / depuis " xyz " cas, ce qui est probablement rarement raisonnable ou correct. c'est-à-dire que vous risquez d'abandonner un type de sécurité utile.

Personnellement, pour l'exemple que vous donnez, je sous-classerais les types rgb et xyz d'une base boost::array<float,3> ou similaire. Ainsi, ils héritent tous les deux de l'opérateur [], peuvent être transmis aux fonctions qui attendent des tableaux et transmis avec davantage de sécurité de type aux objets qui attendent des couleurs / coordonnées. Il est fréquent que vous souhaitiez traiter un xyz ou une RGB comme un tableau, mais rares sont ceux qui souhaitent traiter un xyz comme des RVB ou inversement. (tableau rgb IS-A: OK. xyz tableau IS-A: OK. rgb IS-A xyz ???? Je ne pense pas!)

Bien sûr, cela signifie un accès à x, y, z & amp; r, g, b doivent être effectués par l’accès (transmission au operator[](...) approprié) plutôt que directement au membre. (Vous aurez besoin des propriétés de C # pour cela).

J'ai un modèle et deux classes de vecteur ci-dessous, une folle, une saine. Le modèle implémente un tableau simple de valeurs au moment de la compilation. Il est conçu pour le sous-classement et utilise une variable de tableau protégé pour vous éviter de devoir sauter entre des cerceaux pour accéder au tableau. (Certaines personnes pourraient ne pas aimer une telle conception. Je dis, si vos sous-classes appellent vos opérateurs surchargés, le couplage pourrait être une bonne idée.)

La classe crazy vous permet d’avoir des variables de membre appelées x, y, z et elle agit comme un tableau pour les appels à glGetFloatV. Celui-ci n'a que des fonctions d'accès x (), y (), z () et fonctionne toujours avec glGetFloatV. Vous pouvez utiliser l'une ou l'autre classe comme base pour d'autres objets vectoriels que vous pourriez transmettre à la bibliothèque OpenGL. Bien que les classes ci-dessous soient spécifiques à des points, vous pouvez évidemment effectuer une recherche / remplacement pour les transformer en classes de couleurs RVB.

La classe folle est folle car le coût du sucre syntaxique vec.x au lieu de vec.x () est de 3 variables de référence. Cela pourrait prendre beaucoup de place dans une application volumineuse. Utilisez la version simplifiée de Sane.

template <typename T, int N>
class FixedVector {
protected:
    T arr[N];
public:
    FixedVector();

    FixedVector(const T* a) {
        for (int i = 0; i < N; ++i) {
            arr[i] = a[i];
        }
    }

    FixedVector(const T& other) {
        for (int i = 0; i < N; ++i) {
            arr[i] = other.arr[i];
        }
    }

    FixedVector& operator=(const T& other) {
        for (int i = 0; i < N; ++i) {
            arr[i] = other.arr[i];
        }
        return *this;
    }

    T* operator&() { return arr; }
    const T* operator&() const { return arr; }

    T& operator[](int ofs) { 
        assert(ofs >= 0 && ofs < N);
        return arr[ofs];
    }
    const T& operator[](int ofs) const { 
        assert(ofs >= 0 && ofs < N);
        return arr[ofs];
    }
};

class CrazyPoint :  public FixedVector<float, 3> {
public:
    float &x, &y, &z;

    CrazyPoint()
      : x(arr[0]), y(arr[1]), z(arr[2])
    { arr[0] = arr[1] = arr[2] = 0.0; }

    CrazyPoint(const float* a)
      : x(arr[0]), y(arr[1]), z(arr[2])
    {
        arr[0] = a[0];
        arr[1] = a[1];
        arr[2] = a[2];
    }

    CrazyPoint(float a, float b, float c) 
      : x(a), y(b), z(c)
    {
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
    }
};

class SanePoint : public FixedVector<float, 3> {
public:
    float& x() { return arr[0]; }
    float& y() { return arr[1]; }
    float& z() { return arr[2]; }

    SanePoint() { arr[0] = arr[1] = arr[2] = 0.0; }
    SanePoint(float a, float b, float c) 
    {
        arr[0] = a;
        arr[1] = b;
        arr[2] = c;
    }
};

// usage
SanePoint normal;
glGetFloatV(GL_CURRENT_NORMAL, &normal);

Vous pouvez essayer d'ajouter des références aux variables, comme ceci:

struct test {
        float x, y, z;
        float &r, &g, &b;

        test() : r(x), g(y), b(z) {}
    };

Mais votre structure grossit (de 12 à 40 octets).

Pour utiliser [] dessus, utilisez la surcharge de l'opérateur [], comme mentionné précédemment.

Juste un avertissement sur l'utilisation de membres de référence pointant sur des membres de valeur. Vous devez définir un constructeur de copie (et éventuellement aussi un opérateur d'affectation), si vous copiez un tel objet (comme le transférer par valeur). Le constructeur de copie par défaut vous laissera une copie dont les membres de référence pointent vers les membres de valeur de l'objet d'origine, et non ceux du nouvel objet. Ce n'est certainement pas quelque chose que vous voulez.

Étant donné que vous vous retrouvez également avec des objets plus volumineux, comme indiqué précédemment, l'utilisation de méthodes d'accès est préférable aux membres de référence.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top