Pergunta

Eu tenho certeza que isso é possível, porque eu tenho certeza que eu já vi isso feito. Eu acho que é incrível, mas terei prazer em aceitar respostas ao longo das linhas de "esta é uma ideia terrível, porque ____".

Digamos que temos uma estrutura básica.

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

Agora, eu quero implementar aliases sobre essas variáveis.

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;

O ideal é o terceiro sintaxe seria indistinguível de um array. Isto é, se eu enviar arr como parâmetro de referência para uma função esperando uma matriz de carros alegóricos em que irá armazenar dados (por exemplo, muitas das funções glGet OpenGL), ele iria trabalhar bem.

O que você acha? Possível? Possível, mas estúpido?

Foi útil?

Solução

O que gostaria de fazer é ter acessores:

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_;
}

Outras dicas

Nameless estruturas aninhadas em uma união não são padrão C ++. Isso, no entanto, deve funcionar:

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: Um pouco mais de informação. A estrutura utiliza uma matriz de 3 ponteiro-a-dados-membros para aceder aos dados nos operadores sobrecarregados [].

A linha "typedef flutuar Vertex :: * const vert" significa que vert é um ponteiro para um membro do flutuador da estrutura Vertex. As [3] significa que é uma matriz de três destes. No operador sobrecarregado [], esta matriz é indexado e o ponteiro-para-dados-membro é referência e o valor devolvido.

Além disso, este método deve funcionar independentemente da embalagem questões - o compilador é livre para preencher a estrutura Vertex no entanto, ele gosta e ele ainda vai funcionar muito bem. União anônima vai ter problemas se os carros alegóricos são embalados de forma diferente.

Use uma união?

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

Eu não recomendaria -. Ele vai levar a confusão


Adicionado :

Como observado por Adrian em sua resposta, esta união com os membros de struct anônimos não é suportado pela ISO C ++. Ele funciona em GNU G ++ (com queixas sobre não ser suportados quando você ligar '-Wall -ansi -pedantic'). É uma reminiscência dos C dias pré-pré-padrão (pré-K & R 1º EDN),, quando os nomes de estrutura de elementos tiveram de ser exclusivo em todas as estruturas, e você poderia usar notações contratada para chegar a um deslocamento dentro da estrutura, e você poderia usar nomes de membros de outros tipos de estrutura - uma forma de anarquia. No momento em que eu comecei a usar C (há muito tempo, mas pós-K & R1), que já estava uso histórico.

A notação mostrada com sindicalistas anônimos (para as duas estruturas) é suportado pelo C11 (ISO / IEC 9899: 2011), mas não por versões anteriores do padrão C. Seção 9.5 da ISO / IEC 14882: 2011 (C ++ 11), prevê uniões anônimos, mas GNU g++ (4.9.1) não aceita o código mostrado com -pedantic, identificando "warning: ISO C++ prohibits anonymous structs [-Wpedantic]"

.

Uma vez que a idéia vai levar a confusão, eu não estou particularmente preocupado que não é padrão; Eu não usaria o mecanismo para esta tarefa (e eu ficaria desconfiado de usando estruturas anônimos em uma união mesmo que fosse benéfica).


A preocupação foi levantada:

A três (x-y-z, r-g-b e a matriz) não necessariamente align.

É uma união com três elementos; os três elementos começar no mesmo endereço. Os dois primeiros são estruturas que contêm 3 valores float. Não há nenhuma herança e não há funções virtuais para dar diferentes layouts, etc. As estruturas serão estabelecidos com os três elementos contíguos (na prática, mesmo se o padrão de licenças de preenchimento). A matriz também começa no mesmo endereço, e sujeitas a 'sem preenchimento' nas estruturas, os elementos se sobrepõem as duas estruturas. Eu realmente não ver que não seria um problema.

Você pode obter isso com uma união como os outros. Sobrecarga cor e posição na mesma estrutura como esta pode não ser uma boa idéia (por exemplo, a adição de duas cores normalmente significa que você quer saturar a 1,0, enquanto que a adição de vetores acontece de forma linear), mas sobrepondo um float [] em cima deles como que é perfeitamente bem e um meio bem aceitos de intercâmbio de dados com GL / DirectX / etc.

Eu recomendo que você evitar referindo-se ao mesmo membro por diferentes aliases no mesmo escopo da função, no entanto, porque isso irá levá-lo em uma tenda hardware desagradável chamado uma carga-hit-store. Em particular, evitar isso, se você pode:

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

As referências?

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;
};

Eu acho que você pode fazer alguma mágica macro para obter o que deseja. Mas isso vai olhar feio. Por que você quer usar mesmo struct, vértice por 3 tipos diferentes? Por que você não pode definir classe para a cor? Também tenha em mente que vértice e cor não são os mesmos. Se você mudar alguma coisa para vértice, que irá afectar a cor também, se você tem a mesma classe para ambos.

Eu não tenho certeza se eu entendi a pergunta corretamente. Mas parece que você precisa sobrecarregar o operador [] para fornecer matriz como o acesso ao seu struct / classe. Veja o exemplo mencionado aqui: Operador sobrecarga

A seguir estrutura terá o comportamento solicitado:

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];
    }
};

Bad idéia na minha opinião, pelo menos para o exemplo dado: a desvantagem é que, para praticamente qualquer solução para isso, você provavelmente vai ser capaz de livremente atribuir "RGB" instâncias de / para "xyz" casos, que é provavelmente raramente sensível ou correta. ou seja, o risco de desistir de algumas tipo de segurança útil.

Pessoalmente, para o exemplo que você dá, eu subclasse rgb e tipos xyz de um boost::array<float,3> base ou similar. Então ambos operador herdar [], podem ser passados ??para funções que esperam matrizes, e passou com mais segurança tipo de coisas que esperam cores / coordenadas. É muitas vezes você quiser tratar uma xyz ou um rgb como uma matriz, mas raro você quiser tratar uma xyz como um RGB ou vice-versa. (RGB é-A matriz:. OK xyz IS-A matriz:.! OK RGB é-A xyz ???? Eu não penso assim)

É claro que os meios de acesso para x, y, z & r, g, b tem para ser pelo assessor (encaminhamento para o operator[](...) adequado), em vez de directamente para o membro. (Você precisa de propriedades 's C # para isso).

Eu tenho um modelo e duas classes Vector abaixo, um louco, um sã. Os instrumentos molde uma simples fixado em matriz de tempo de compilação de valores. Ele é projetado para subclassificação e usa uma variável de matriz protegido para evitar que você tenha que saltar através de aros para acessar a matriz. (Algumas pessoas podem não como o projeto tal. Eu digo, se suas subclasses estão chamando seus operadores sobrecarregados, o acoplamento pode ser uma boa idéia.)

A classe louco permite que você tenha variáveis ??de membro chamada x, y, z e ele age como uma matriz para chamadas para glGetFloatv. A um são apenas tem assessor funções x (), y (), Z () e ainda funciona com glGetFloatv. Você pode usar qualquer classe de base para outros objetos vetoriais que você pode passar para a biblioteca OpenGL. Embora as classes abaixo são específicos para pontos, você pode, obviamente, basta fazer uma pesquisa / substituir a transformá-los em algumas classes de cor RGB.

A classe louco é louco, porque o custo do açúcar sintático vec.x vez de vec.x () é de 3 variáveis ??de referência. Isso poderia levar até um monte de espaço em uma grande aplicação. Use a versão mais simples são.

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);

Você pode tentar adicionar referências a variáveis, como este:

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

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

Mas a sua estrutura se torna maior (a partir de 12 bytes para 40 bytes).

Para usar [] sobre ele, o uso sobrecarga de operador [], como mencionado anteriormente.

Apenas um aviso sobre o uso de membros de referência apontando para membros de valor. Você precisa definir um construtor de cópia (e possivelmente também operador de atribuição), se você nunca copiar um objeto tal (como transferência, por valor). O construtor de cópia padrão vai deixar você com uma cópia cujos membros referência apontar para os membros valor do objeto original, e não as do novo objeto. Isto certamente não é algo que você quer.

Considerando que você também acabar com objetos maiores, como já foi salientado, Acho que usando métodos de acesso deve ser preferido sobre os membros de referência.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top