Wpis z mikrobloga

Chciałem zrobić taką "sztuczkę" w C++ - przyjąć w konstruktorze klasy/struktury, wartość typu T1, w jakiś sposób "zapamiętać" typ przyjęty przez konstruktor, a następnie w innej funkcji sprawdzić, czy inny podany typ T2 == T1.

Spróbowałem zrobić to w ten sposób:

struct Foo {
        std::size_t type;


        template
        Foo(T value) : type(typeid(T).hash_code()) {}
``````
        template
        const bool is_same() {
                return typeid(T).hash_code() == type;
        }
};
``````
Foo bar(3.14f);
const bool same = bar.is_same();
std::cout << same; // 1

To działa poprawnie(pomijając const, bo typeid ma z tym problem), ale czy nie dałoby się tego wykonać lepiej, np. używając constexpr, aby identyfikatory typów(typeid(T).hash_code()), nawet tylko tych fundamentalnych, były wprowadzane statycznie, na etapie kompilacji(czyli nie za pomocą typeid, ale jakoś inaczej), a nie dopiero w trakcie działania programu?

#programowanie #cpp + specjaliści: @KrzaQ2, @lionbest ( ͡° ͜ʖ ͡°)
  • 17
@inplaz: no właśnie, ja nie chcę porównywać typów na etapie kompilacji, ale zrobić tak, aby typeid(T).hash_code() było rozwiązywane na etapie kompilacji, ale z tego co wiem, typeid jest zawsze run-time.

@KrzaQ2: myślałem czy da się zaimplementować coś w rodzaju boost::any bez użycia typeid.
Na przykład tak:

struct Any {
        char value[8]; // oczywiscie tu powinna byc jakas dedukcja najwiekszego mozliwego rozmiaru prymitywnego typu


        template 
        Any(T v) {
                *((T *)&value) = v;
        }
``````
        template 
        const T get() {
                return *((T *)&value);
@KrzaQ2: nie mogę użyć variant, bo przecież wtedy nie będę w stanie tego użyć w std::vectorze lub innym kontenerze, jeśli chcę, aby Any mógł przechowywać dowolny typ.

Załóżmy taką sytuację:

using Settings = std::map;
Settings settings{
        { "foo", 3.14f },        // float (4)
        { "bar", 6.28 },         // double (8)
        { "xyz", "hello" }       // const char * (8)
}


try {
        const auto s = settings["xyz"].get();
}
catch (... ex) {}
@KrzaQ2: działający przykład z użyciem typeid:

struct Any {
        static constexpr int INVALID_TYPE = 0;


        char value[8];
        std::size_t type;
``````
        template 
        Any(T v) : type(typeid(T).hash_code()) {
                *((T *)&value) = v;
        }
``````
        template 
        const T get() {
                if (typeid(T).hash_code() != type)
                        throw INVALID_TYPE;
``````
                return *((T *)&value);
        }
};
``````
Any foo(3.14f);
``````
try {
        const auto value = foo.get();
} catch (...) {}
@5z7k9: Wołam jeszcze raz bo jak geniusz podlinkowałem starą wersję. Teraz działa static_assert(x.get() == 42);, ale nie dałbym tego na produkcję bez gruntownego code review (którego by nie przeszło bo imo dość brzydkie, chociaż działa...)
@KrzaQ2: O ile dobrze widzę to będzie działało tylko dla typów z static types?

@5z7k9: Od C++11 jest do tego typeindex, bo sam hashcode() ci nie wystarczy (może się okazać że dwie mają taki sam), ale fakt faktem nie da się tego zainicjalizować jako constexpr czyli
constexpr Foo bar(3.14f);
nie zadziała.
Generalnie typeid(T) nigdy nie będzie constexpr, nie da się tego tak zaimplementować, no chyba że ograniczasz
@KrzaQ2: @lionbest: znalazłem ten post i pomyślałem, że może się nadać. Tutaj przykładowy kod takiego Any, właśnie według tego counter patternu: http://melpon.org/wandbox/permlink/8zd6MLuavTHCkOaN
Co najlepsze, wystarczy -O1 i gcc już na etapie kompilacji wie, czy typy się zgadzają(żadne cmp nie pojawia się w pliku wynikowym, również jeśli typy się zgadzają). Link do godbolta: https://godbolt.org/g/Xr7BYL

PS: W tym wypadku nie da się zrobić static_assert, ale C++ uczę się od
5z7k9 - @KrzaQ2: @lionbest: znalazłem ten post i pomyślałem, że może się nadać. Tutaj...

źródło: comment_tOZnhTWtYyMqihPoLv4pEa89M4gG7wxI.jpg

Pobierz
@5z7k9:

no chyba że ograniczasz się do jednej jednostki kompilacji


Dokładnie mi o ten sposób chodziło. Lecz tutaj nie możesz założyć że inne pliki C++ będą miały tą samą kolejność typów. Po za tym to wyższa magia i nie działa na wszystkich kompilatorach tak samo i ciężko powiedzieć czy to nie precyzyjność standardu czy bugi w kompilatorach.