Skip to content

Commit

Permalink
Support indirection in C++ static reflection
Browse files Browse the repository at this point in the history
Summary:
[Thrift] Support indirection in C++ static reflection.

Indirection is done with a type alias with special annotations:

```
typedef i32 (cpp.type = 'CppTypeWrappingI32', cpp.indirection = '.chain().of.member().accesses') WrappedI32

struct HasOneIntegralField {
  1: WrappedI32 integral_field,
}
```

Previously, `reflected_struct_data_member</*...*/>::getter` had no way to see the wrapped object. And code using it had no way to know, supposing the field type is a wrapper, how to access the wrapped object.

Here, we change the `getter` to see through any wrappers to access the wrapped object directly.

However, this leaves reflection without any way to access the wrapper object. Tradeoffs.

Reviewed By: juchem

Differential Revision: D4406389

fbshipit-source-id: 0904e9fdefb55f1e0c1a48899d76c1d76659b8c8
  • Loading branch information
yfeldblum authored and facebook-github-bot committed Jan 13, 2017
1 parent 1b74346 commit 6804932
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 6 deletions.
40 changes: 38 additions & 2 deletions thrift/compiler/py/generate/t_cpp_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5148,6 +5148,8 @@ def _generate_fatal_struct_impl(self, sns, program):
safe_ns = self._get_namespace().replace('.', '_')
dtmclsprefix = '{0}_{1}__struct_unique_data_member_getters_list'.format(
safe_ns, name)
indclsprefix = '{0}_{1}__struct_unique_indirections_list'.format(
safe_ns, name)
mpdclsprefix = '{0}_{1}__struct_unique_member_pod_list'.format(
safe_ns, name)
annclsprefix = '{0}_{1}__struct_unique_annotations'.format(
Expand Down Expand Up @@ -5187,6 +5189,26 @@ def _generate_fatal_struct_impl(self, sns, program):
with detail.cls('struct {0}'.format(dtmclsprefix)).scope as dtm:
for i in members:
dtm('FATAL_DATA_MEMBER_GETTER({0}, {0});'.format(i))
for i in structs:
if i.is_union:
continue
if any(self._type_access_suffix(m.type) for m in i.members):
with detail.cls('struct {0}_{1}'.format(
i.name, indclsprefix)).scope as ind:
for m in i.members:
note = self._type_access_suffix(m.type)
if not note:
continue
with ind.cls('struct {0}'.format(
m.name)).scope as ind:
out('template <typename T{0}>\n'
'static auto val(T{0} &&{0}) {{\n'
' return std::forward<T{0}>({0}){1};\n'
'}}'.format('__thrift__arg__', note))
out('template <typename T{0}>\n'
'static auto &&ref(T{0} &&{0}) {{\n'
' return std::forward<T{0}>({0}){1};\n'
'}}'.format('__thrift__arg__', note))
with detail.cls('struct {0}'.format(mpdclsprefix)).scope as mpd:
pod_arg = 'T_{0}_{1}_struct_member_pod'.format(safe_ns, name)
for i in members:
Expand Down Expand Up @@ -5231,8 +5253,22 @@ def _generate_fatal_struct_impl(self, sns, program):
cmnf(' {0},'.format(m.key))
cmnf(' ::apache::thrift::optionality::{0},'.format(
self._render_fatal_required_qualifier(m.req)))
cmnf(' {0}::{1}::{2},'.format(
self.fatal_detail_ns, dtmclsprefix, m.name))
if self._type_access_suffix(m.type):
cmnf(' ::fatal::chained_data_member_getter<')
cmnf(' {0}::{1}::{2},'.format(
self.fatal_detail_ns, dtmclsprefix, m.name))
cmnf(' ::apache::thrift::detail::'
'reflection_indirection_getter<')
cmnf(' {0}::{1}_{2}::{3}'.format(
self.fatal_detail_ns,
i.name,
indclsprefix,
m.name))
cmnf(' >')
cmnf(' >,')
else:
cmnf(' {0}::{1}::{2},'.format(
self.fatal_detail_ns, dtmclsprefix, m.name))
cmnf(' {0},'.format(
self._render_fatal_type_class(m.type)))
cmnf(' {0}::{1}::{2}_{3}_struct_member_pod_{4},'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,30 @@ struct test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list {
FATAL_DATA_MEMBER_GETTER(result, result);
};

struct struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_indirections_list {
struct number {
template <typename T__thrift__arg__>
static auto val(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).number;
}
template <typename T__thrift__arg__>
static auto &&ref(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).number;
}
};

struct result {
template <typename T__thrift__arg__>
static auto val(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).foo().result();
}
template <typename T__thrift__arg__>
static auto &&ref(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).foo().result();
}
};
};

struct test_cpp2_cpp_reflection_module__struct_unique_member_pod_list {
template <typename T_test_cpp2_cpp_reflection_module_struct_member_pod>

Expand Down Expand Up @@ -3386,7 +3410,12 @@ struct struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_m
::test_cpp2::cpp_reflection::HasANumber,
3,
::apache::thrift::optionality::required_of_writer,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::number,
::fatal::chained_data_member_getter<
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::number,
::apache::thrift::reflection_indirection_getter<
thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_indirections_list::number
>
>,
::apache::thrift::type_class::integral,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_member_pod_list::test_cpp2_cpp_reflection_module_struct_member_pod_number,
::apache::thrift::reflected_annotations<thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_annotations::members::number>,
Expand All @@ -3398,7 +3427,12 @@ struct struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_m
::test_cpp2::cpp_reflection::HasAResult,
4,
::apache::thrift::optionality::required_of_writer,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::result,
::fatal::chained_data_member_getter<
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::result,
::apache::thrift::reflection_indirection_getter<
thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_indirections_list::result
>
>,
::apache::thrift::type_class::integral,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_member_pod_list::test_cpp2_cpp_reflection_module_struct_member_pod_result,
::apache::thrift::reflected_annotations<thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_annotations::members::result>,
Expand Down
38 changes: 36 additions & 2 deletions thrift/compiler/test/fixtures/fatal/gen-cpp2/module_fatal_struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,30 @@ struct test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list {
FATAL_DATA_MEMBER_GETTER(result, result);
};

struct struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_indirections_list {
struct number {
template <typename T__thrift__arg__>
static auto val(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).number;
}
template <typename T__thrift__arg__>
static auto &&ref(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).number;
}
};

struct result {
template <typename T__thrift__arg__>
static auto val(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).foo().result();
}
template <typename T__thrift__arg__>
static auto &&ref(T__thrift__arg__ &&__thrift__arg__) {
return std::forward<T__thrift__arg__>(__thrift__arg__).foo().result();
}
};
};

struct test_cpp2_cpp_reflection_module__struct_unique_member_pod_list {
template <typename T_test_cpp2_cpp_reflection_module_struct_member_pod>

Expand Down Expand Up @@ -3535,7 +3559,12 @@ struct struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_m
::test_cpp2::cpp_reflection::HasANumber,
3,
::apache::thrift::optionality::required_of_writer,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::number,
::fatal::chained_data_member_getter<
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::number,
::apache::thrift::reflection_indirection_getter<
thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_indirections_list::number
>
>,
::apache::thrift::type_class::integral,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_member_pod_list::test_cpp2_cpp_reflection_module_struct_member_pod_number,
::apache::thrift::reflected_annotations<thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_annotations::members::number>,
Expand All @@ -3547,7 +3576,12 @@ struct struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_m
::test_cpp2::cpp_reflection::HasAResult,
4,
::apache::thrift::optionality::required_of_writer,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::result,
::fatal::chained_data_member_getter<
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_data_member_getters_list::result,
::apache::thrift::reflection_indirection_getter<
thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_indirections_list::result
>
>,
::apache::thrift::type_class::integral,
thrift_fatal_impl_detail::test_cpp2_cpp_reflection_module__struct_unique_member_pod_list::test_cpp2_cpp_reflection_module_struct_member_pod_result,
::apache::thrift::reflected_annotations<thrift_fatal_impl_detail::struct_with_indirections_test_cpp2_cpp_reflection_module__struct_unique_annotations::members::result>,
Expand Down
14 changes: 14 additions & 0 deletions thrift/lib/cpp2/fatal/internal/reflection-inl-post.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ struct mark_set<Owner, Getter, false> {

} // reflection_impl

template <typename Impl>
struct reflection_indirection_getter {
template <typename T>
using type = decltype(Impl::val(std::declval<T &&>()));

template <typename T>
using reference = decltype(Impl::ref(std::declval<T &&>()));

template <typename T>
static auto &&ref(T &&arg) {
return Impl::ref(std::forward<T>(arg));
}
};

template <typename Module, typename Annotations, legacy_type_id_t LegacyTypeId>
struct type_common_metadata_impl {
using module = Module;
Expand Down
1 change: 1 addition & 0 deletions thrift/lib/cpp2/fatal/reflection.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <fatal/type/transform.h>
#include <fatal/type/variant_traits.h>

#include <utility>
#include <type_traits>

#include <cstdint>
Expand Down
15 changes: 15 additions & 0 deletions thrift/test/fatal_reflection_indirection.thrift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cpp_include "thrift/test/fatal_reflection_indirection_types.h"

namespace cpp2 reflection_indirection

typedef i32 (cpp.type = 'CppFakeI32') FakeI32
typedef i32 (cpp.type = 'CppHasANumber', cpp.indirection = '.number') HasANumber
typedef i32 (cpp.type = 'CppHasAResult', cpp.indirection = '.foo().result()')
HasAResult

struct struct_with_indirections {
1: i32 real,
2: FakeI32 fake,
3: HasANumber number,
4: HasAResult result,
}
83 changes: 83 additions & 0 deletions thrift/test/fatal_reflection_indirection_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2017 Facebook, Inc.
*
* 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.
*/

#include "thrift/test/gen-cpp2/fatal_reflection_indirection_fatal.h"

#include <gtest/gtest.h>

#include <folly/Utility.h>
#include <folly/Traits.h>
#include <thrift/lib/cpp2/fatal/internal/test_helpers.h>

namespace {

class FatalReflectionIndirectionTest : public testing::Test {};
}

using type = reflection_indirection::struct_with_indirections;
using info = apache::thrift::reflect_struct<type>;

TEST_F(FatalReflectionIndirectionTest, sanity_check_no_indirection) {
type obj;
obj.real = 12;
EXPECT_EQ(12, obj.real);
}

TEST_F(FatalReflectionIndirectionTest, simple_alias_no_indirection) {
type obj;
obj.fake = 15;
EXPECT_EQ(15, obj.fake);
}

TEST_F(FatalReflectionIndirectionTest, indirection_via_single_member_field) {
using getter = info::member::number::getter;
EXPECT_SAME<
std::int32_t &,
decltype(getter::ref(std::declval<type &>()))
>();
EXPECT_SAME<
std::int32_t &&,
decltype(getter::ref(std::declval<type &&>()))
>();
EXPECT_SAME<
std::int32_t const&,
decltype(getter::ref(std::declval<type const&>()))
>();

type obj;
obj.number.number = -43;
EXPECT_EQ(-43, getter::ref(obj));
}

TEST_F(FatalReflectionIndirectionTest, indirection_via_chained_member_funcs) {
using getter = info::member::result::getter;
EXPECT_SAME<
std::int32_t &,
decltype(getter::ref(std::declval<type &>()))
>();
EXPECT_SAME<
std::int32_t &&,
decltype(getter::ref(std::declval<type &&>()))
>();
EXPECT_SAME<
std::int32_t const&,
decltype(getter::ref(std::declval<type const&>()))
>();

type obj;
obj.result.foo().result() = -2;
EXPECT_EQ(-2, getter::ref(obj));
}
64 changes: 64 additions & 0 deletions thrift/test/fatal_reflection_indirection_types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2017 Facebook, Inc.
*
* 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.
*/

#pragma once

#include <cstdint>

namespace reflection_indirection {

using CppFakeI32 = std::int32_t;

struct CppHasANumber {
std::int32_t number{};
CppHasANumber() {}
explicit CppHasANumber(std::int32_t number_) : number(number_) {}
bool operator==(CppHasANumber that) const { return number == that.number; }
bool operator!=(CppHasANumber that) const { return number != that.number; }
};

class CppHasAResult {
public:
class Foo {
public:
explicit Foo(std::int32_t& obj) : obj_(obj) {}
auto& result() & { return obj_; }
auto&& result() && { return std::move(obj_); }
auto const& result() const& { return obj_; }
private:
std::int32_t& obj_;
};

CppHasAResult() {}
explicit CppHasAResult(std::int32_t result) : result_(result) {}
CppHasAResult(CppHasAResult const& that) : result_(that.result_) {}
CppHasAResult& operator=(CppHasAResult const& that) {
this->~CppHasAResult();
return *::new (this) CppHasAResult(that);
}

bool operator==(CppHasAResult that) const { return result_ == that.result_; }
bool operator!=(CppHasAResult that) const { return result_ != that.result_; }

Foo& foo() & { return foo_; }
Foo&& foo() && { return static_cast<Foo&&>(foo_); }
Foo const& foo() const& { return foo_; }

private:
std::int32_t result_{};
Foo foo_{result_};
};
}

0 comments on commit 6804932

Please sign in to comment.