C++程序  |  188行  |  6.3 KB

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef IORAP_COMMON_INTROSPECTION_H
#define IORAP_COMMON_INTROSPECTION_H

/*
 * Provide zero-cost compile-time introspection of struct member fields.
 *
 * Example:
 *
 * // Declaration
 * struct PackageEvent {
 *
 *   int type;
 *   std::string package_uri;
 *   std::string package_name;
 * };
 *
 * IORAP_INTROSPECT_ADAPT_STRUCT(PackageEvent, type, package_uri, package_name);
 *
 * // Usage
 * {
 *   std::stringstream str;
 *   for_each_member_field(PackageEvent{123,"hello","world"}, [&](auto&& val) {
 *     str << val << ",";
 *   }
 *   CHECK_EQ("123,hello,world,"s, str.str());
 * }
 */

#include "common/macros.h"
#include "common/type.h"

#include <tuple>

namespace iorap {
namespace introspect {

template <auto value>
struct member_type;

// Compile-time introspection data for a member-to-pointer.
//
// Example:
//   using package_uri_member_type = member_type<&PackageEvent::&package_uri>
//   int type = package_uri_member_type::value(PackageEvent{123,"hello","world"});
//   CHECK_EQ(type, 123);
template <typename T, typename F, F T::*member>
struct member_type<member> {
  // The type of the struct this field is located in, e.g. 'struct XYZ {...}' -> XYZ.
  static constexpr auto struct_t = type_c<T>;
  // The type of the field, e.g. 'struct XYZ { int x; }' -> int.
  static constexpr auto type = type_c<F>;

  // Allow a 'const U', 'volatile U', 'U&' etc here.
  // Returns the value inside of 'U'.
  template <typename U>
  static constexpr decltype(auto) value(U&& v) {
    static_assert(std::is_same_v<T, std::decay_t<U>>, "U must be cvref of T");

    using U_noref = std::remove_reference_t<U>;

    // This casts from the regular non-const pointer-to-member to a potentially const/volatile
    // pointer-to-member.
    F U_noref::*safer_member = member;

    // Now dereference it,
    return v.*safer_member;
    // TODO: are we properly returning && for rvalue, & for lvalue refs, etc?
  }

  static constexpr void set_value(typename decltype(struct_t)::type& s,
                                  typename decltype(type)::type&& value) {
    s.*member = std::forward<typename decltype(type)::type>(value);
  }
};

// Given a self : T, where T has introspection-enabled support, T has some
// members m1, m2, m3, ... , mN.
//
// Invokes fun(self.*m1); fun(self.*m2); fun(self.*m3); ... ; fun(self.*mN).
template <typename T, typename F>
static constexpr void for_each_member_field_value(T&& self, F&& fun) {
  constexpr auto members = introspect_members(type_c<std::decay_t<T>>);
  // std::tuple<member_type<A>, member_type<B>, ...>

  // Warning: Don't use 'v=std::forward<V>(v)' as that actually captures-by-value.
  for_each(members, [&fun, &self](auto&& type) mutable {
    // Note that 'type' is a member_type
    fun(type.value(std::forward<T>(self)));
  });
}

// Given a self : T, where T has introspection-enabled support, T has some
// members m1, m2, m3, ... , mN. The basic_type of each member is t1, t2, t3, ..., tN.
//
// Invokes
//   self.*m1 = fun(self, t1);
//   self.*m2 = fun(self, t2);
//   self.*m3 = fun(self, t3);
//   ...;
//   self.*mN = fun(self, tN).
template <typename T, typename F>
static constexpr void for_each_member_field_set_value(T&& self, F&& fun) {
  constexpr auto members = introspect_members(type_c<std::decay_t<T>>);
  // std::tuple<member_type<A>, member_type<B>, ...>

  // Warning: Don't use 'v=std::forward<V>(v)' as that actually captures-by-value.
  for_each(members, [&fun, &self](auto&& type) mutable {
    // Note that 'type' is a member_type
    type.set_value(std::forward<T>(self), fun(type.type));
  });
}

}
}

// Add compile-time introspection capabilities to a pre-existing struct or class.
//
// Arguments: Name, [Member1, Member2, ... MemberN]
//
// Example:
//
//   struct Rectangle {
//     int height;
//     int width;
//   };
//
//   IORAP_INTROSPECT_ADAPT_STRUCT(Rectangle, height, width);
//
// See also for_each_member_field_value.
#define IORAP_INTROSPECT_ADAPT_STRUCT(/*name, [member1, member2, member3, ...]*/...) \
  IORAP_INTROSPECT_ADAPT_STRUCT_IMPL(IORAP_PP_NARG(__VA_ARGS__), __VA_ARGS__)

#define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL(N, ...) \
  IORAP_PP_CONCAT(IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_, N)(__VA_ARGS__)

// This simple implementation relies on the 'introspect_members' function being overloaded
// for the type<T> values. ADL is then applied to resolve the exact overload for any T,
// thus allowing this function definition to be in any namespace.

// The auto signature must conform to:
//   introspect_members(type<T>) -> std::tuple<member_type1, member_type_2, ...>

// TODO: it would be nice to capture the name of the member as a string literal.
#define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_1(TYPE) \
  static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
    return std::make_tuple();\
  }
#define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_2(TYPE, m1) \
  static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
    return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{}\
    );\
  }

#define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_3(TYPE, m1, m2) \
  static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
    return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{},\
                           ::iorap::introspect::member_type<&TYPE::m2>{}\
    ); \
  }

#define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_4(TYPE, m1, m2, m3) \
  static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
    return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{},\
                           ::iorap::introspect::member_type<&TYPE::m2>{},\
                           ::iorap::introspect::member_type<&TYPE::m3>{}\
    ); \
  }

// TODO: Consider using IORAP_PP_MAP


#endif  // IORAP_COMMON_INTROSPECTION_H