Дополнительная возможность структурной типизации в C ++ или любом другом языке? - PullRequest
3 голосов
/ 02 июня 2010

В C ++, как сказать компилятору, что Ogre :: Vector3 IS_SAME_AS SomeOtherLIB :: Vector3? Я чувствую это ... в таких языках, как с ++, которые не являются структурно типизированными, но в некоторых случаях это имеет смысл.

Обычно в качестве разработчика игр при работе с 4+ библиотеками, которые предоставляют сортировку или собственную реализацию Vector3. Код замусорен функциями преобразования ToOgre, ToThis, ToThat. Это большое копирование Float3, которое не должно происходить на первом месте.

Имеется в C ++ или любых других языках, где нам не нужно преобразовывать (копировать) из одного типа в другой, что по сути одно и то же Но любое решение в C ++, как и большинство хороших библиотек gamedev, предназначено для c / c ++.

Ответы [ 4 ]

17 голосов
/ 02 июня 2010

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

class Foo { void quack() {} };
class Bar { void quack() {} };
class Baz {};

template<typename Duck>
void f(Duck d) {
    d.quack();
}
int main() {
    f(Foo()); // works
    f(Bar()); // works
    f(Baz()); // compile error because Baz does not have a quack method
    return 0;
}
6 голосов
/ 02 июня 2010

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

Допустим, у вас есть два типа вектора:

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

struct Vec3B {
    float p[3];
};

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

template<class T> float get_x(const T&);
template<class T> float get_y(const T&);
template<class T> float get_z(const T&);

template<> float get_x<Vec3A>(const Vec3A& v) { return v.x; }
// ...
template<> float get_x<Vec3B>(const Vec3B& v) { return v.p[0]; }
// ...

С такими помощниками вы можете теперь написать универсальные функции, которые работают на обоих:

template<class T> float length(const T& t) {
    return std::sqrt(std::pow(get_x(t), 2), 
                     std::pow(get_y(t), 2),
                     std::pow(get_z(t), 2));
}

Вы также можете продолжить, специализируя такие утилиты, как length(), для повышения производительности или по другим причинам, например. если определенный вектор уже имеет функцию-член, предоставляющую вам длину:

template<> float length<Vec3C>(const Vec3C& v) {
    return v.length();
}
2 голосов
/ 02 июня 2010

Если вы действительно уверены в случае не виртуальных структур, вы можете сделать reinterpret_cast. Однако лучше:

  1. выполняет шаблонные функции-оболочки, как показано sepp2k
  2. наследуется от одного из векторов и добавляет оператор преобразования в другой вектор
  3. добавить отдельную функцию _cast, которая выполняет преобразование
1 голос
/ 07 апреля 2013

Haxe - это очень переносимый язык с полностью необязательным структурным подтипом:

typedef Vector3 = { x : double, y : double, z : double };

class FancyVector3 {
    public var x : double, y : double, z : double;

    function dot(Vector3 v) {
        return x * v.x + y * v.y + z * v.z;
    }

    function length() {
        return Math.sqrt(dot(this));
    }
}

Vector3 не только является уже используемой структурой, но и выступает в качестве структурного интерфейса для других классов. Такие typedef структуры могут указывать сигнатуры функций, а также поля.

У Haxe также есть CFFI для общения с C ++ (хотя он все еще требует методов преобразования), и привязки уже существуют для нескольких игровых движков C ++, а также для различных низкоуровневых сред. Также разрабатываются кроссплатформенные движки, написанные на чистом Haxe, предназначенные для C ++, Flash и JS (как Canvas, так и WebGL).

Возможно, это не то решение, которое вы ищете прямо сейчас, но может стать интереснее через несколько лет.

...