Skip to content

Commit 1ff5f5d

Browse files
vitautfacebook-github-bot
authored andcommitted
Move prototype database next to prototype for reusability
Summary: Move `prototype_database` to `whisker/object.h`, next to other prototype APIs, because we need to reuse it when constructing mstch objects. There is nothing specific to generators in it anyway. Reviewed By: yoney, praihan Differential Revision: D71203061 fbshipit-source-id: 5214dd2cd43d99dfaf6830155a670e5c8f58b999
1 parent fa6aa63 commit 1ff5f5d

File tree

2 files changed

+110
-116
lines changed

2 files changed

+110
-116
lines changed

third-party/thrift/src/thrift/compiler/generate/t_whisker_generator.h

+1-116
Original file line numberDiff line numberDiff line change
@@ -51,19 +51,11 @@
5151
#include <thrift/compiler/ast/t_union.h>
5252
#include <thrift/compiler/whisker/dsl.h>
5353

54-
#include <fmt/core.h>
55-
56-
#include <boost/core/demangle.hpp>
57-
5854
#include <filesystem>
5955
#include <functional>
6056
#include <map>
61-
#include <stdexcept>
6257
#include <string>
6358
#include <string_view>
64-
#include <typeindex>
65-
#include <typeinfo>
66-
#include <unordered_map>
6759

6860
namespace apache::thrift::compiler {
6961

@@ -186,114 +178,7 @@ class t_whisker_generator : public t_generator {
186178
using prototype = whisker::prototype<T>;
187179
template <typename T = void>
188180
using prototype_ptr = whisker::prototype_ptr<T>;
189-
190-
/**
191-
* The prototype database stores and caches prototype indexed by typeid.
192-
*
193-
* This is primarily used for the `make_prototype_for*` family of functions
194-
* below.
195-
*/
196-
class prototype_database {
197-
public:
198-
template <typename T>
199-
void define(prototype_ptr<T> prototype) {
200-
auto [_, inserted] =
201-
prototypes_.emplace(std::type_index(typeid(T)), std::move(prototype));
202-
if (!inserted) {
203-
throw std::runtime_error(fmt::format(
204-
"Prototype for type '{}' already exists.",
205-
boost::core::demangle(typeid(T).name())));
206-
}
207-
}
208-
209-
/**
210-
* Gets the cached prototype for the given type, or throws an exception if
211-
* the type is unknown.
212-
*
213-
* If allow_lazy is true, then a failed lookup falls back to a "lazy"
214-
* prototype which is resolved when used. This is helpful when there is a
215-
* cycle of type references.
216-
*/
217-
template <typename T>
218-
prototype_ptr<T> of(bool allow_lazy = true) const {
219-
auto found = prototypes_.find(std::type_index(typeid(T)));
220-
if (found == prototypes_.end()) {
221-
if (allow_lazy) {
222-
return this->lazy<T>();
223-
}
224-
throw std::runtime_error(fmt::format(
225-
"Prototype for type '{}' does not exist.",
226-
boost::core::demangle(typeid(T).name())));
227-
}
228-
auto casted =
229-
std::dynamic_pointer_cast<const prototype<T>>(found->second);
230-
if (casted == nullptr) {
231-
throw std::runtime_error(fmt::format(
232-
"Prototype for type '{}' is of an unexpected type.",
233-
typeid(T).name()));
234-
}
235-
return casted;
236-
}
237-
238-
/**
239-
* Creates a native_handle for the given reference with a prototype stored
240-
* in this database.
241-
*
242-
* std::remove_reference_t<T> forces the caller to explicitly specify the
243-
* template argument. This is to prevent accidental use of the wrong type.
244-
*/
245-
template <typename T>
246-
whisker::native_handle<T> create(
247-
whisker::managed_ptr<std::remove_reference_t<T>> o) const {
248-
return whisker::native_handle<T>(std::move(o), of<T>());
249-
}
250-
template <typename T>
251-
whisker::native_handle<T> create(
252-
const std::remove_reference_t<T>& o) const {
253-
return this->create<T>(whisker::manage_as_static(o));
254-
}
255-
template <typename T>
256-
whisker::object create_nullable(const std::remove_reference_t<T>* o) const {
257-
return o == nullptr ? whisker::make::null
258-
: whisker::object(this->create<T>(*o));
259-
}
260-
261-
/**
262-
* A "lazy" prototype is one whose definition can be deferred until first
263-
* use. This allows prototypes to refer to each other in cycles that have
264-
* cyclic references.
265-
*
266-
* Note that cyclical prototypes chains are still disallowed.
267-
*/
268-
template <typename T>
269-
prototype_ptr<T> lazy() const {
270-
class lazy_prototype final : public prototype<T> {
271-
public:
272-
explicit lazy_prototype(const prototype_database& db) : db_(db) {}
273-
274-
const prototype<>::descriptor* find_descriptor(
275-
std::string_view name) const final {
276-
return this->resolve()->find_descriptor(name);
277-
}
278-
std::set<std::string> keys() const final {
279-
return this->resolve()->keys();
280-
}
281-
const prototype<>::ptr& parent() const final {
282-
return this->resolve()->parent();
283-
}
284-
285-
private:
286-
prototype_ptr<T> resolve() const {
287-
return db_.of<T>(false /* allow_lazy */);
288-
}
289-
const prototype_database& db_;
290-
};
291-
return std::make_shared<const lazy_prototype>(*this);
292-
}
293-
294-
private:
295-
std::unordered_map<std::type_index, whisker::prototype<>::ptr> prototypes_;
296-
};
181+
using prototype_database = whisker::prototype_database;
297182

298183
/**
299184
* Registers the `make_prototype_for_*` functions with the prototype database.

third-party/thrift/src/thrift/compiler/whisker/object.h

+109
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <set>
2828
#include <string>
2929
#include <string_view>
30+
#include <typeindex>
3031
#include <typeinfo>
3132
#include <unordered_map>
3233
#include <utility>
@@ -42,6 +43,8 @@
4243

4344
#include <fmt/core.h>
4445

46+
#include <boost/core/demangle.hpp>
47+
4548
namespace whisker {
4649

4750
// Whisker supports a small set of types.
@@ -1426,4 +1429,110 @@ object native_handle(whisker::native_handle<T> handle) {
14261429

14271430
} // namespace make
14281431

1432+
/**
1433+
* The prototype database stores and caches prototype indexed by typeid.
1434+
*
1435+
* This allows reusing prototypes when dealing with recursive native types such
1436+
* as AST classes.
1437+
*/
1438+
class prototype_database {
1439+
public:
1440+
template <typename T>
1441+
void define(prototype_ptr<T> prototype) {
1442+
auto [_, inserted] =
1443+
prototypes_.emplace(std::type_index(typeid(T)), std::move(prototype));
1444+
if (!inserted) {
1445+
throw std::runtime_error(fmt::format(
1446+
"Prototype for type '{}' already exists.",
1447+
boost::core::demangle(typeid(T).name())));
1448+
}
1449+
}
1450+
1451+
/**
1452+
* Gets the cached prototype for the given type, or throws an exception if
1453+
* the type is unknown.
1454+
*
1455+
* If allow_lazy is true, then a failed lookup falls back to a "lazy"
1456+
* prototype which is resolved when used. This is helpful when there is a
1457+
* cycle of type references.
1458+
*/
1459+
template <typename T>
1460+
prototype_ptr<T> of(bool allow_lazy = true) const {
1461+
auto found = prototypes_.find(std::type_index(typeid(T)));
1462+
if (found == prototypes_.end()) {
1463+
if (allow_lazy) {
1464+
return this->lazy<T>();
1465+
}
1466+
throw std::runtime_error(fmt::format(
1467+
"Prototype for type '{}' does not exist.",
1468+
boost::core::demangle(typeid(T).name())));
1469+
}
1470+
auto casted = std::dynamic_pointer_cast<const prototype<T>>(found->second);
1471+
if (casted == nullptr) {
1472+
throw std::runtime_error(fmt::format(
1473+
"Prototype for type '{}' is of an unexpected type.",
1474+
typeid(T).name()));
1475+
}
1476+
return casted;
1477+
}
1478+
1479+
/**
1480+
* Creates a native_handle for the given reference with a prototype stored
1481+
* in this database.
1482+
*
1483+
* std::remove_reference_t<T> forces the caller to explicitly specify the
1484+
* template argument. This is to prevent accidental use of the wrong type.
1485+
*/
1486+
template <typename T>
1487+
whisker::native_handle<T> create(
1488+
whisker::managed_ptr<std::remove_reference_t<T>> o) const {
1489+
return whisker::native_handle<T>(std::move(o), of<T>());
1490+
}
1491+
template <typename T>
1492+
whisker::native_handle<T> create(const std::remove_reference_t<T>& o) const {
1493+
return this->create<T>(whisker::manage_as_static(o));
1494+
}
1495+
template <typename T>
1496+
whisker::object create_nullable(const std::remove_reference_t<T>* o) const {
1497+
return o == nullptr ? whisker::make::null
1498+
: whisker::object(this->create<T>(*o));
1499+
}
1500+
1501+
/**
1502+
* A "lazy" prototype is one whose definition can be deferred until first
1503+
* use. This allows prototypes to refer to each other in cycles that have
1504+
* cyclic references.
1505+
*
1506+
* Note that cyclical prototypes chains are still disallowed.
1507+
*/
1508+
template <typename T>
1509+
prototype_ptr<T> lazy() const {
1510+
class lazy_prototype final : public prototype<T> {
1511+
public:
1512+
explicit lazy_prototype(const prototype_database& db) : db_(db) {}
1513+
1514+
const prototype<>::descriptor* find_descriptor(
1515+
std::string_view name) const final {
1516+
return this->resolve()->find_descriptor(name);
1517+
}
1518+
std::set<std::string> keys() const final {
1519+
return this->resolve()->keys();
1520+
}
1521+
const prototype<>::ptr& parent() const final {
1522+
return this->resolve()->parent();
1523+
}
1524+
1525+
private:
1526+
prototype_ptr<T> resolve() const {
1527+
return db_.of<T>(false /* allow_lazy */);
1528+
}
1529+
const prototype_database& db_;
1530+
};
1531+
return std::make_shared<const lazy_prototype>(*this);
1532+
}
1533+
1534+
private:
1535+
std::unordered_map<std::type_index, whisker::prototype<>::ptr> prototypes_;
1536+
};
1537+
14291538
} // namespace whisker

0 commit comments

Comments
 (0)