Информация об изменениях

Сообщение "Динамическая" структура на C++ от 11.12.2019 2:46

Изменено 12.12.2019 7:01 alexanderfedin

"Динамическая" структура на C++
Исходник здесь: https://onlinegdb.com/B1KuXCT6H

  Позволяет задать тип структуры прямо в месте ее использования и дать возможность копировать такие структуры между собой:
#include <stdio.h>
#include <type_traits>
#include <iostream>

#include "record.hpp"
#include "find_prop_type.hpp"

enum class f_int { v };
enum class f_float { v };
enum class f_str { v };
enum class f_rec { v };
enum class f_unknown { v };

enum class f_data {v};
enum class f_next {v};

using namespace tensors::type_map;
using namespace tensors::structural;

template <typename T>    
struct ll_node
    : virtual public record<prop<f_data, T>, prop<f_next, ll_node<T> *>>
{
    ll_node()
    {
        (*this)[f_next::v] = nullptr;
    }
};

int main()
{
    using namespace std;
    
    cout << "Looking for " << typeid(f_int).name() << endl;
    using found1 = 
    find_prop_type<
        f_int,
        prop<f_int, int>,
        prop<f_float, float>
    >;
    found1::print(std::cout);
    cout << "Value type found: " << typeid(value_type_of<typename found1::prop_type>).name() << endl;
    cout << endl;
    
    cout << "Looking for " << typeid(f_float).name() << endl;
    using found2 =
    find_prop_type<
        f_float,
        prop<f_int, int>,
        prop<f_float, float>
    >;
    found2::print(std::cout);
    cout << "Value type found: " << typeid(value_type_of<typename found2::prop_type>).name() << endl;
    cout << endl;
    
    cout << "Looking for " << typeid(f_unknown).name() << endl;
    using found3 =
    find_prop_type<
        f_unknown,
        prop<f_int, int>,
        prop<f_float, float>
    >;
    found3::print(std::cout);
    cout << "Value type found: " << typeid(value_type_of<typename found3::prop_type>).name() << endl;
    cout << endl;
    
    record
    <
        prop<f_int, int>,
        prop<f_float, float>,
        prop
        <
            f_rec,
            record
            <
                prop<f_str, const char *>,
                prop<f_int, int>
            >
        >
    > r1;
    r1[f_int::v] = 123;
    r1[f_float::v] = 2.7;
    r1[f_rec::v][f_str::v] = "Hello World!";
    
    //this structure has fields order changed and one of the fields removed
    //but still compatible with the first one for assignment
    record
    <
        prop<f_float, float>,
        prop<f_int, int>,
        prop
        <
            f_rec,
            record
            <
                // prop<f_int, int>,
                prop<f_str, const char *>
            >
        >
    > r2;
    r2 = r1;
    r2[f_rec::v][f_str::v] = "Goodbye World!";
    
    cout << "r1: " << typeid(f_int).name() << " == " << r1[f_int::v] << endl;
    cout << "r1: " << typeid(f_float).name() << " == " << r1[f_float::v] << endl;
    cout << "r1: " << typeid(f_rec).name() << "." << typeid(f_str).name() << " == " << r1[f_rec::v][f_str::v] << endl;
    cout << endl;
    cout << "r2: " << typeid(f_int).name() << " == " << r2[f_int::v] << endl;
    cout << "r2: " << typeid(f_float).name() << " == " << r2[f_float::v] << endl;
    cout << "r2: " << typeid(f_rec).name() << "." << typeid(f_str).name() << " == " << r2[f_rec::v][f_str::v] << endl;
    cout << endl;
    
    using lln = ll_node<int>;
    lln *head = nullptr;
    for (auto i = 0; i < 5; ++i)
    {
        lln *n = new lln;
        (*n)[f_next::v] = head;
        (*n)[f_data::v] = i;
        head = n;
    }

    for (lln *n = head; n; n = (*n)[f_next::v])
    {
        cout << (*n)[f_data::v] << endl;
    }

    return 0;
}

Делал как часть библиотеки поддержки транслятора кода Матлаб в CUDA C++, в котором структуры чисто динамические.
  Имена полей могут задаваться тоже абсолютно динамическим способом:
#include <iostream>
#include <typeinfo>

template <typename T, T... chars>
struct c_str
{
    constexpr c_str() noexcept {}
    static constexpr const auto &v = *static_cast<const c_str<T, chars...> *>(nullptr);
};

template <typename T, T... chars>
const T *get_name(const c_str<T, chars...> &)
{
    static constexpr const T value[] = { chars..., T() };
    return value;
}

using f_surname = c_str<char, 's', 'u', 'r', 'n', 'a', 'm', 'e'>;
using f_surname = c_str<char, 's', 'u', 'r', 'n', 'a', 'm', 'e'>;

using f_lastname = c_str<char, 'l', 'a', 's', 't', 'n', 'a', 'm', 'e'>;


int main()
{
    std::cout << typeid(f_surname).name() << " : " << get_name(f_surname::v) << std::endl;
    std::cout << typeid(f_lastname).name() << " : " << get_name(f_lastname::v) << std::endl;
    
    return 0;
}

Как раз для этих имен полей мне и нужна была специализация класса строковым литералом, чтобы в сгенерированном исходнике C++ текст смотрелся более прилично.
Буст использовать не могу, поскольку для этого мне придется скопировать кучу исходников и добавить туда __device__ __host__ атрибуты.
Так что пока я забил на это.
"Динамическая" структура на C++
Исходник здесь: https://onlinegdb.com/SywcaRcpr

  Позволяет задать тип структуры прямо в месте ее использования и дать возможность копировать такие структуры между собой:
#include <stdio.h>
#include <type_traits>
#include <iostream>

#include "record.hpp"
#include "find_prop_type.hpp"

enum class f_int { v };
enum class f_float { v };
enum class f_str { v };
enum class f_rec { v };
enum class f_unknown { v };

enum class f_data {v};
enum class f_next {v};

using namespace tensors::type_map;
using namespace tensors::structural;

template <typename T>    
struct ll_node
    : virtual public record<prop<f_data, T>, prop<f_next, ll_node<T> *>>
{
    ll_node()
    {
        (*this)[f_next::v] = nullptr;
    }
};

int main()
{
    using namespace std;
    
    cout << "Looking for " << typeid(f_int).name() << endl;
    using found1 = 
    find_prop_type<
        f_int,
        prop<f_int, int>,
        prop<f_float, float>
    >;
    found1::print(std::cout);
    cout << "Value type found: " << typeid(value_type_of<typename found1::prop_type>).name() << endl;
    cout << endl;
    
    cout << "Looking for " << typeid(f_float).name() << endl;
    using found2 =
    find_prop_type<
        f_float,
        prop<f_int, int>,
        prop<f_float, float>
    >;
    found2::print(std::cout);
    cout << "Value type found: " << typeid(value_type_of<typename found2::prop_type>).name() << endl;
    cout << endl;
    
    cout << "Looking for " << typeid(f_unknown).name() << endl;
    using found3 =
    find_prop_type<
        f_unknown,
        prop<f_int, int>,
        prop<f_float, float>
    >;
    found3::print(std::cout);
    cout << "Value type found: " << typeid(value_type_of<typename found3::prop_type>).name() << endl;
    cout << endl;
    
    record
    <
        prop<f_int, int>,
        prop<f_float, float>,
        prop
        <
            f_rec,
            record
            <
                prop<f_str, const char *>,
                prop<f_int, int>
            >
        >
    > r1;
    r1[f_int::v] = 123;
    r1[f_float::v] = 2.7;
    r1[f_rec::v][f_str::v] = "Hello World!";
    
    //this structure has fields order changed and one of the fields removed
    //but still compatible with the first one for assignment
    record
    <
        prop<f_float, float>,
        prop<f_int, int>,
        prop
        <
            f_rec,
            record
            <
                // prop<f_int, int>,
                prop<f_str, const char *>
            >
        >
    > r2;
    r2 = r1;
    r2[f_rec::v][f_str::v] = "Goodbye World!";
    
    cout << "r1: " << typeid(f_int).name() << " == " << r1[f_int::v] << endl;
    cout << "r1: " << typeid(f_float).name() << " == " << r1[f_float::v] << endl;
    cout << "r1: " << typeid(f_rec).name() << "." << typeid(f_str).name() << " == " << r1[f_rec::v][f_str::v] << endl;
    cout << endl;
    cout << "r2: " << typeid(f_int).name() << " == " << r2[f_int::v] << endl;
    cout << "r2: " << typeid(f_float).name() << " == " << r2[f_float::v] << endl;
    cout << "r2: " << typeid(f_rec).name() << "." << typeid(f_str).name() << " == " << r2[f_rec::v][f_str::v] << endl;
    cout << endl;
    
    using lln = ll_node<int>;
    lln *head = nullptr;
    for (auto i = 0; i < 5; ++i)
    {
        lln *n = new lln;
        (*n)[f_next::v] = head;
        (*n)[f_data::v] = i;
        head = n;
    }

    for (lln *n = head; n; n = (*n)[f_next::v])
    {
        cout << (*n)[f_data::v] << endl;
    }

    return 0;
}

Делал как часть библиотеки поддержки транслятора кода Матлаб в CUDA C++, в котором структуры чисто динамические.
  Имена полей могут задаваться тоже абсолютно динамическим способом:
#include <iostream>
#include <typeinfo>

template <typename T, T... chars>
struct c_str
{
    constexpr c_str() noexcept {}
    static constexpr const auto &v = *static_cast<const c_str<T, chars...> *>(nullptr);
};

template <typename T, T... chars>
const T *get_name(const c_str<T, chars...> &)
{
    static constexpr const T value[] = { chars..., T() };
    return value;
}

using f_surname = c_str<char, 's', 'u', 'r', 'n', 'a', 'm', 'e'>;
using f_surname = c_str<char, 's', 'u', 'r', 'n', 'a', 'm', 'e'>;

using f_lastname = c_str<char, 'l', 'a', 's', 't', 'n', 'a', 'm', 'e'>;


int main()
{
    std::cout << typeid(f_surname).name() << " : " << get_name(f_surname::v) << std::endl;
    std::cout << typeid(f_lastname).name() << " : " << get_name(f_lastname::v) << std::endl;
    
    return 0;
}

Как раз для этих имен полей мне и нужна была специализация класса строковым литералом, чтобы в сгенерированном исходнике C++ текст смотрелся более прилично.
Буст использовать не могу, поскольку для этого мне придется скопировать кучу исходников и добавить туда __device__ __host__ атрибуты.
Так что пока я забил на это.