简体   繁体   中英

C++ template meta-programming, number of member variables?

Is it possible in C++ to determine number of variables/fields in the generic class? for example

// suppose I need metaclass number_members determines number of members

struct example { int i, j; };
assert(number_members<example>::value==2);

I looked through mpl but could not find implementation.

thanks.

No. C++ does not provide general introspection into structures.

You can try a C ++0x std::tuple , which has some of the features of a general POD struct . Or, try to roll your own from the Boost MPL library. That would be a bit advanced if you're just getting started with C++.

No. Unfortunately, C++ does not have that kind of introspection builtin. However, with some additional preprocessing such as Qt 's Meta Object Compiler (moc) , you can achieve something similar... the QMetaObject class provides a propertyCount() ; however, your class would need to inherit from QObject , use the Q_OBJECT macro, and register the properties for all that to work... so, in short, it's not automatic.

You can't do that directly. The obvious question then, is what you're trying to accomplish -- chances are that you can do what you need to, but the way to do it may be rather different.

Yes, with restrictions. You can find my implementation at mattkretz/virtools . It requires C++20, because it uses concepts. In principle, you can rewrite it using enable_if and thus make it work with C++17. Live example .

The main idea here is to restrict the set of types that can be inspected to aggregates with non-static data members either in a single base class or only in the derived class (but still allowing empty base classes). This is the same restriction structured bindings imposes. Then you know whether a type T has eg 3 (or more) members if T{anything_but_base_of<T>(), anything_but_base_of<T>(), anything_but_base_of<T>()} is a valid expression (ie no substitution failure). Where anything_but_base_of is:

template <class Struct> struct anything_but_base_of {
  template <class T>
  requires(!std::is_base_of_v<T, Struct>)
  operator T();
};

Since aggregate initialization allows to specify fewer initializers than the aggregate has members one has to test starting from an upper bound and then recurse down to 0 until a possible aggregate initialization is found. The destructuring test my implementation uses is actually not a SFINAE condition, but rather produces a hard error. So you can just as well remove that code, making the implementation:

namespace detail {
template <class Struct> struct any_empty_base_of {
  template <class T>
  requires(std::is_base_of_v<T, Struct> && std::is_empty_v<T>)
  operator T();
};

template <class T, size_t... Indexes>
concept brace_constructible =
   requires { T{((void)Indexes, anything_but_base_of<T>())...}; } 
|| requires { T{any_empty_base_of<T>(), ((void)Indexes, anything_but_base_of<T>())...}; }
|| requires { T{any_empty_base_of<T>(), any_empty_base_of<T>(), ((void)Indexes, anything_but_base_of<T>())...}; }
|| requires { T{any_empty_base_of<T>(), any_empty_base_of<T>(), any_empty_base_of<T>(), ((void)Indexes, anything_but_base_of<T>())...}; };

template <class T, size_t... Indexes>
requires brace_constructible<T, Indexes...>
constexpr size_t struct_size(std::index_sequence<Indexes...>)
{
  return sizeof...(Indexes);
}

template <class T>
requires requires { T{}; }
constexpr size_t struct_size(std::index_sequence<>)
{
  static_assert(std::is_empty_v<T>,
                "Increase MaxSize on your struct_size call. (Or you found a bug)");
  return 0;
}

template <class T, size_t I0, size_t... Indexes>
requires(!brace_constructible<T, I0, Indexes...>)
constexpr size_t struct_size(std::index_sequence<I0, Indexes...>)
{
  // recurse with one less initializer
  return struct_size<T>(std::index_sequence<Indexes...>());
}
} // namespace detail

Finally, we need a sensible upper bound to start from. The correct upper bound would be sizeof(T) * CHAR_BIT , for the case of a bitfield where each non-static data member occupies a single bit and the whole struct contains no padding. Considering the compile time cost of using the correct upper bound, I settled on a more sensible heuristic of simply sizeof(T) :

template <typename T, size_t MaxSize = sizeof(T)>
constexpr inline std::size_t struct_size =
    detail::struct_size<T>(std::make_index_sequence<MaxSize>());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM