Git Product home page Git Product logo

Comments (7)

veselink1 avatar veselink1 commented on June 1, 2024 1

Hi @RalphSteinhagen, I'm considering ways of implementing this, some along the lines of what you suggested, but I do not have a clear timeline just yet.

from refl-cpp.

veselink1 avatar veselink1 commented on June 1, 2024

REFL_TYPE generates a specialisation of a hidden type called refl_impl::metadata::type_info__. Specialisations cannot be declared from within another non-enclosing namespace in standard C++. There are compiler extensions that allow that. IIRC, I could only get it to work on GCC with -fpermissive, I believe, but changes at the refl-cpp level would be necessary as well.

Happy holidays to you as well!

from refl-cpp.

RalphSteinhagen avatar RalphSteinhagen commented on June 1, 2024

@veselink1 any chance of addressing this issue?

We would like to use your little library in a wider range of projects (beyond our OpenCMW library), notably the new GNU Radio 4.0. Having to define the reflection macros always in the root namespace and not in the nested namespace where the aggregate type is defined is sort of a usability issue and error-prone/hard to explain to novice or occasional C++ developers.

At its core, the issue as you mentioned is:

namespace library { // library namespace -- cannot touch
  template <typename T>
  struct my_struct {};
}

// user code -- needs nested namespace
namespace user { 
  template<>
  struct library::my_struct<int> { // error: class template specialization of 'my_struct' not in a namespace enclosing 'library'
    int data;
  };
}

template<>
struct library::my_struct<double> { // valid
    double data;
};

At the same time, Niels Lohmann's Json library uses two similar types of macros NLOHMANN_DEFINE_TYPE_INTRUSIVE (declared within the class) and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE (declared outside the class) where the namespacing doesn't seem to be an issue.

Is the difference because refl-cpp defines a templated struct that is being specialised for each user type and the other lib a templated function? πŸ€”

Could we iterate on this and -- if you like -- maybe find a solution together?

from refl-cpp.

RalphSteinhagen avatar RalphSteinhagen commented on June 1, 2024

@veselink1 I may have found one possible solution to the problem above. Plasese see the compiler-explorer example for details.

The problem with template specialisation is that it needs to be done in the same and/or root namespace. However, this isn't necessary for function specialisation as commonly used, for example, by overloading the operator<< operator. Using this as an inspiration, one could create the refl_impl::metadata::type_info__ from within that function like (const T& is just needed for the template ADL):

namespace library {
template <typename T, typename... Members>
struct type_info {
    using value_type = T;
    constexpr static std::size_t N = sizeof...(Members);
    constexpr static std::string_view nameSpace = CURRENT_NAMESPACE;
    std::string_view className;    
    std::array<std::string_view, N> memberName;
    std::tuple<Members T::*...> memberReference;
};

template <typename T>
consteval static library::type_info<T> typeInfo(const T&) {
    return library::type_info<T>{
        .className = {"<unknown type>"}};
}
} // namespace library

and helper macros as (N.B. here w/o the macro expansions (wanted to keep is KISS)):

#define ENABLE_REFLECTION(...) ENABLE_REFLECTION_N(__VA_ARGS__, ENABLE_REFLECTION3, ENABLE_REFLECTION2, ENABLE_REFLECTION1)(__VA_ARGS__)

#define ENABLE_REFLECTION_N(_1, _2, _3, _4, NAME, ...) NAME

#define ENABLE_REFLECTION1(TypeName, Member1)  \
consteval static library::type_info<TypeName, decltype(std::declval<TypeName>().Member1)> typeInfo(const TypeName&) {    \
  return library::type_info<TypeName, decltype(std::declval<TypeName>().Member1)>{ \
    .className  = {#TypeName}, \
    .memberName = { #Member1 }, \
    .memberReference = std::make_tuple(&TypeName::Member1) \
    };  \
}
// [...]

This would allow the following:

namespace ns1 {
struct MyClass {
    int fieldA = 42;
    float fieldB = 3.14f;
};
ENABLE_REFLECTION(MyClass, fieldA, fieldB);

}  // namespace ns1

namespace ns2 {
struct MyClass {
    int value = 99;
};
ENABLE_REFLECTION(MyClass, value);

}  // namespace ns2

// global namespace
struct MyClass {
    int age = 99;
    std::string name = "pops";
};
ENABLE_REFLECTION(MyClass, age, name);

struct UnknownClass {
    int value = 123;
    float value2 = 1.23f;
};

template<typename T>
constexpr void print_type_info() {
    using namespace library;
    T data{};
    constexpr auto type_info = typeInfo(data);
    fmt::print("{}::{}\n", type_info.nameSpace, type_info.className);
    std::apply([&type_info, &data](auto&&... args) {
        std::size_t i = 0;
        ( (fmt::print("  {} = {}\n", type_info.memberName[i++], data.*args)), ...);
    }, type_info.memberReference);
    fmt::print("\n");
}

int main() {

    print_type_info<MyClass>();
    print_type_info<UnknownClass>();
    print_type_info<ns1::MyClass>();
    print_type_info<ns2::MyClass>();

    return 0;
}

printout:

::MyClass
  age = 99
  name = pops

library::<unknown type>

ns1::MyClass
  fieldA = 42
  fieldB = 3.14

ns2::MyClass
  value = 99

What do you think?

N.B. I played also a bit with structured bindings but this, unfortunately, fails once classes have parents. πŸ€”

For some weird reason (<->a hole in the C++ std) one can use the brace-initialiser with classes inheritance but not the other way around with the structured bindings.

from refl-cpp.

veselink1 avatar veselink1 commented on June 1, 2024

Hi @RalphSteinhagen,

Thanks for sharing your findings. The approach you've linked to is one that I am aware of and have considered both when writing the original implementation and when you've previously asked about this.

Where it breaks down is the inability to have a template method inside of a local class.

See this example: https://godbolt.org/z/9YWjGc41P

This is something that is used pervasively in the generated type and member descriptors.

from refl-cpp.

RalphSteinhagen avatar RalphSteinhagen commented on June 1, 2024

Does one really need templated functions inside the struct?

The access could be also a free-standing functions as illustrated in the second code-snippet I posted (albeit in tiny script). The free standing function could then access the tuple as long as it's publically defined:

template <std::size_t idx, typename Type>
auto& member(Type&& type) {
    return std::get<idx>(to_tuple(std::forward<Type&>(type)));
}

The to_tuple(...) is just syntactic sugar using structured bindings in case the members are not explicitly declared as done in the first sample as

std::tuple<Members T::*...> type_info<T>::memberReference

That could work, couldn't it?

from refl-cpp.

RalphSteinhagen avatar RalphSteinhagen commented on June 1, 2024

@veselink1 could we iterate on the above proposal? Maybe a direct chat/video/group call would be useful?

from refl-cpp.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.