-1

After reading this source (and also my answer) and this source, I got the impression that we can use std::source_location::function_name to extract names of data members.

Let's say we are given some struct_t type with an instance of my_struct. Further, the following properties are valid for struct_t:

  1. The number of non-static data members is 3;
  2. The following line compiles successfully and has the expected behavior:auto& [a,b,c] = my_struct;;
  3. Any additional assumptions if this is really necessary

Q: How to use C++20 without other libs and std::source_location::function_name to extract all non-static data member names from struct_t? The correct answer should be easily generalized to an arbitrary number of fields and also be compatible with g++, clang, and MSVC.

The code below shows that this task is quite possible. But first, it requires a copy constructor and is also not compatible with MSVC, which means it violates the C++20 language standard. Your answer may use this code as a starting point or not use it at all

#include <iostream>
#include <type_traits>
#include <source_location>
#include <vector>
#include <string_view>
#include <array>    

struct struct_t {
    int field1;
    double field2;
    std::vector<int> field3;
};

template<auto p>
std::string_view get(){
      return std::source_location::current().function_name();
}

template<class T>
auto fields3(T&& st){
    static auto inst{std::forward<T>(st)};//bad. Needs a copy or move constructor 
    auto& [a,b,c]=inst;
    return std::array{get<&a>(), get<&b>(), get<&c>()};
}

int main()
{
    for (auto field:fields3(struct_t{})){
        std::cout<<field<<"\n";
    }
    //std::string_view get() [p = &inst.field1]
    //std::string_view get() [p = &inst.field2]
    //std::string_view get() [p = &inst.field3]
    return 0;
}
12
  • why does it "look strange" ? Where does it compile? not here godbolt.org/z/T9xzPGed8 Commented May 16, 2024 at 17:45
  • @463035818_is_not_an_ai Please check again Commented May 16, 2024 at 17:51
  • 1
    is that the question? how to compile this with msvc? Please make a focused specific question. I really dont know what you are looking for exactly Commented May 16, 2024 at 17:54
  • 1
    Boost.PFR already does this. You can analyze their source, or just use that lib directly. Commented May 16, 2024 at 17:57
  • 1
    actually no, I honestly do not understand what answer you expect. You link to a library that has all the code ready, you just need to read it. Doing it again from scratch makes a nice exercise, but then my question remains, what is the next step you want to take? Make it compile with msvc? Get it to work with other structs with arbitrary number of members? something else? Commented May 16, 2024 at 18:16

1 Answer 1

2

Here is the approach the linked library seems to take:

template <typename T>
struct Wrapper
{
    const T value;
    static const Wrapper<T> fake;
};

template <typename T>
consteval const T& get_fake()
{
    return Wrapper<T>::fake.value;
}

struct Tester
{
    Tester() = delete;
    Tester(const Tester&) = delete;
};

struct Foo
{
    Tester test;
};

template <auto... Args>
consteval std::string_view get_name()
{
    return std::source_location::current().function_name();
}

template <typename T>
consteval auto members_tuple()
{
    const auto& [a] = get_fake<T>();
    return std::tie(a);
}

template <typename T>
consteval std::string_view get_name_binding()
{
    constexpr auto members = members_tuple<T>();
    return get_name<&std::get<0>(members)>();
}

int main()
{
    std::cout << get_name_binding<Foo>();
}

Demo

The basic strategy seems to be to pass pointers to fields of a static member object that's declared but never defined. As such, the object is never actually constructed. Accessing the fields of such an object is OK in this very specific circumstance though, so yes, this is standard-compiliant as far as I can tell.

Is it portable though? No. This relies on parsing the string returned by std::source_location::current().function_name(), which is implementation-defined and may or may not actually contain the names of the fields pointed to by the pointers passed to it. On all three major compilers it does (though MSVC is a bit picky and sometimes doesn't depending on the exact context), but that isn't required.

Sign up to request clarification or add additional context in comments.

4 Comments

This is an interesting approach. But it seems that the structured binding is no working, i.e. auto& [a]=get_fake<Foo>(); std::cout << get_name<&a>();
Yes, a would need to be a compile-time constant to be a valid template parameter. GCC and Clang both seem to be happy if the structured binding is done in a consteval context, but MSVC isn't. I'm not entirely sure who's correct here.
So what can we do if the structure type is set as a template parameter? I mean a function with such a signature template<class T> void print_fields3()
I updated the answer with a version that works with structured bindings. Instead of using the binding directly it packs it into a constexpr tuple of references and then std::gets them back out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.