C++Guns – RoboBlog

01.09.2017

C++ Guns - C++17 Extension to aggregate initialization

Filed under: Allgemein — Thomas @ 14:09

C++17 bringt ein paar tolle Sachen mit. Mit der Erweiterten Aggregat Initialisierung kann ich nun endlich meinen Lieblings Point3D Datentyp zusammenbauen.
Dieser soll mit den Funktionen x(), y() z() Zugriff auf die einzelnen Skalare bieten, aber gleichzeitig ein Array fester Länge sein. Damit der Compiler besser optimieren kann. Der Typ selbst soll ein Array sein, und nicht ein Array als Membervariable enthalten, um weniger tippen zu müssen. Gleichzeitig soll aber eine Initialisierung ohne selbst geschriebenen Konstruktor möglich sein. Der Typ muss für die Performance natürlich POD sein, darf aber niemals uninitialisiert sein. Der interne Type der Skalare muss per Templates austauschbar sein. Um Einheiten wie z.B. Meter oder Grad zu verwenden. Es sollen mit Point3D Berechnungen zur Compilezeit möglich sein, also constexpr. Eine Ausgabe über cout und Standard Operatoren wie + - * / sollen zur Verfügung stehen.

Ganz schön viele Sachen. Aber das lässt sich alles realisieren:

Getestet mit gcc 7.1.0 und clang-4.
Point3D.cpp.gz

#include <iostream>
#include <array>
#include <type_traits>

template<typename T>
struct Point3D : public std::array<T,3> // Typ selbst ist ein Array fester Laenge
{
    using value_type = T;

    Point3D() = delete;

    constexpr const T& x() const {
        return this->at(0);
    }

    constexpr T& x() {
        return this->at(0);
    }

    constexpr const T& y() const {
        return this->at(1);
    }

    constexpr T& y() {
        return this->at(1);
    }

    constexpr const T& z() const {
        return this->at(2);
    }

    constexpr T& z() {
        return this->at(2);
    }
};

// interner Type austauschbar
using Point3Dd = Point3D<double>;
using Point3Df = Point3D<float>;
using Point3Di = Point3D<int>;

static_assert(std::is_pod<Point3Dd>::value, "Point3D is not POD");

template<typename T>
std::ostream& operator<<(std::ostream& s, const Point3D<T>& p) {
    s << '(' << p[0] << ' ' << p[1] << ' ' << p[2] << ')';
    return s;
}

template<typename T, typename T2>
constexpr auto operator+(const Point3D<T>& p1, const Point3D<T2>& p2) {
    using Internal = decltype(T() + T2());
    return Point3D<Internal> {p1[0]+p2[0], p1[1]+p2[1], p1[2]+p2[2]};
}

template<typename T, typename T2>
constexpr auto operator-(const Point3D<T>& p1, const Point3D<T2>& p2) {
    using Internal = decltype(T() - T2());
    return Point3D<Internal> {p1[0]-p2[0], p1[1]-p2[1], p1[2]-p2[2]};
}

// Rest fuer dich

int main() {
    // niemals uninitialisiert
    // error: use of deleted function ‘Point3D<T>::Point3D()
    //Point3Dd p;

    // Initialisierung ohne selbst geschriebenen Konstruktor
    constexpr Point3Di p1{1, 2, 3};

    // Ausfuerung zur Compilezeit
    static_assert(p1.x() + p1.y() + p1.z() == 6, "Oh oh");

    std::cout << p1 << "\n";

    // (Physikalische) Einheiten nutzbar (kommt bald)
    //Point3D<SI::Meter> p2 {1_m, 100_cm, 0.001_km);

    constexpr Point3Dd p3{1.0, 2.0, 3.0};
    // int + double = ? Der Compiler weis es
    constexpr auto p4 = p1+p3;

    static_assert(std::is_same<decltype(p4)::value_type, double>::value, "int+double should be double. right?");
}

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress