Skip to content

Commit

Permalink
Consolidate annotations documentation
Browse files Browse the repository at this point in the history
Summary: Move documentation for structured annotations to their docblocks, transcluded into one page in static docs that also holds legacy content. Merge other two pages into this one.

Reviewed By: vitaut

Differential Revision: D51532913

fbshipit-source-id: df5c57c7dc772f7b75cafb5c8565814c2eba9391
  • Loading branch information
iahs authored and facebook-github-bot committed Nov 28, 2023
1 parent 7c6f412 commit 110b603
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 94 deletions.
97 changes: 88 additions & 9 deletions thrift/annotation/cpp.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,23 @@ namespace py.asyncio facebook_thrift_asyncio.annotation.cpp
namespace go thrift.annotation.cpp
namespace py thrift.annotation.cpp

// start

/**
* Changes the native type of a Thrift object.
* `name` and `template` correspond to `cpp.type` and `cpp.template` respecively.
* Changes the native type of a Thrift object (the C++ type used in codegen) to the value of the `name` field.
* Container types may instead provide the `template` field, in which case template parameters will be filled in by thrift.
* (e.g. `template = "folly::sorted_vector_set"` is equivalent to `type = "folly::sorted_vector_set<T>"` on `set<T>`)
*
* It is also possible to add `cpp_include` to bring in additional data structures and use them here.
* It is required that the custom type matches the specified Thrift type even for internal container types.
* Prefer types that can leverage `reserve(size_t)` as Thrift makes uses these optimizations.
* *Special Case*: This annotation can be used to define a string/binary type as `IOBuf` or `unique_ptr<IOBuf>` so that you can leverage Thrift's support for zero-copy buffer manipulation through `IOBuf`.
* During deserialization, thrift receives a buffer that is used to allocate the appropriate fields in the struct. When using smart pointers, instead of making a copy of the data, it only modifies the pointer to point to the address that is used by the buffer.
*
* The custom type must provide the following methods
* * `list`: `push_back(T)`
* * `map`: `insert(std::pair<T1, T2>)`
* * `set`: `insert(T)`
*/
@scope.Typedef
@scope.Field
Expand All @@ -36,25 +50,51 @@ struct Type {
2: string template (cpp.name = "template_");
}

enum RefType {
Unique = 0,
Shared = 1,
SharedMutable = 2,
}

/**
* Allocates a field on the heap instead of inline.
* This annotation is added to support recursive types. However, you can also use it to turn a field from a value to a smart pointer.
* `@cpp.Ref` is equivalent having type`@cpp.RefType.Unique`.
*
* NOTE: A struct may transitively contain itself as a field only if at least one of the fields in the inclusion chain is either an optional Ref field or a container. Otherwise the struct would have infinite size.
*/
@scope.Field
struct Ref {
1: RefType type;
1: RefType type; /// Optional, defaults to Unique
}
enum RefType {
Unique = 0, /// `std::unique_ptr<T>`
Shared = 1, /// `std::shared_ptr<const T> `
SharedMutable = 2, /// `std::shared_ptr<T>`
}

/**
* Changes the name of the definition in generated C++ code.
* In most cases a much better solution is to rename the problematic Thrift field itself. Only use the `cpp.name` annotation if such renaming is problematic,
* e.g. when the field name appears in code as a string, particularly when using JSON serialization, and it is hard to change all usage sites.
*/
@scope.Definition
struct Name {
1: string value;
}

/**
Lazily deserialize large field on first access.
```
FooWithLazyField foo;
apache::thrift::CompactSerializer::deserialize(serializedData, foo);
// large_field is lazy field, it will be deserialized on first access
// The data will be deserialized in method call large_field_ref()
LOG(INFO) << foo.large_field_ref()->size();
// Result will be cached, we won't deserialize again
LOG(INFO) << foo.large_field_ref()->size();
```
Read more: /doc/fb/languages/cpp/lazy.md
*/

@scope.Field
struct Lazy {
// Use std::unique_ptr<folly::IOBuf> instead of folly::IOBuf to store serialized data.
Expand Down Expand Up @@ -136,11 +176,43 @@ struct Adapter {
5: bool moveOnly;
}

/**
* Packs isset bits into fewer bytes to save space at the cost of making access more expensive.
* Passing `atomic = false` reduces the access cost while making concurrent writes UB.
* Read more: /doc/fb/languages/cpp/isset-bitpacking.md
*/
@scope.Struct
struct PackIsset {
1: bool atomic = true;
}

/**
This annotation enables reordering of fields in the generated C++ struct to minimize padding.
This is achieved by placing the fields in the order of decreasing alignments. The order of fields with the same alignment is preserved.
```
@cpp.MinimizePadding
struct Padded {
1: byte small
2: i64 big
3: i16 medium
4: i32 biggish
5: byte tiny
}
```
For example, the C++ fields for the `Padded` Thrift struct above will be generated in the following order:
```
int64_t big;
int32_t biggish;
int16_t medium;
int8_t small;
int8_t tiny;
```
which gives the size of 16 bytes compared to 32 bytes if `cpp.MinimizePadding` was not specified.
*/
@scope.Struct
struct MinimizePadding {}

Expand Down Expand Up @@ -215,6 +287,7 @@ struct UseOpEncode {}
/**
* Enum in C++ by default uses signed 32 bit integer. There is no need to specify
* underlying type for signed 32 bit integer.
* 64-bit is not supported to avoid truncation since enums are sent as 32-bit integers over the wire.
*/
enum EnumUnderlyingType {
/** ::std::int8_t */
Expand Down Expand Up @@ -291,6 +364,12 @@ struct GenerateTypedInterceptor {}
* Causes C++ handler code to run inline on the EventBase thread.
* Disables overload protection, use with caution.
* Cannot be applied to individual functions in interactions.
*
* Causes the request to be executed on the event base thread directly instead of rescheduling onto a thread manager thread, provided the async_eb_ handler method is implemented.
* You should only execute the request on the event base thread if it is very fast and you have measured that rescheduling is a substantial chunk of your service's CPU usage.
* If a request executing on the event base thread blocks or takes a long time, all other requests sharing the same event base are affected and latency will increase significantly.
* We strongly discourage the use of this annotation unless strictly necessary. You will have to implement the harder-to-use async_eb_ handler method.
* This also disables queue timeouts, an important form of overload protection.
*/
@scope.Function
@scope.Interaction
Expand Down
145 changes: 103 additions & 42 deletions thrift/annotation/hack.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -24,57 +24,80 @@ namespace py.asyncio facebook_thrift_asyncio.annotation.hack
namespace go thrift.annotation.hack
namespace hs2 facebook.thrift.annotation.hack

// An experimental annotation that applies a Hack wrapper to fields.
// For example:
//
// struct User {
// @hack.FieldWrapper{name="MyWrapper"}
// 1: i64 id;
// }
//
// start

/// An experimental annotation that applies a Hack wrapper to fields.
/// For example:
///
/// struct User {
/// @hack.FieldWrapper{name="MyWrapper"}
/// 1: i64 id;
/// }
@scope.Field
struct FieldWrapper {
// The name of a Hack wrapper class used to wrap the field
/// The name of a Hack wrapper class used to wrap the field
1: string name;
}

// An annotation that applies a Hack wrapper to fields, typedef or structs.
// For example:
//
// struct User {
// @hack.FieldWrapper{name="MyWrapper"}
// 1: i64 id;
// }
//
/// An annotation that applies a Hack wrapper to fields, typedef or structs.
/// For example:
///
/// struct User {
/// @hack.FieldWrapper{name="MyWrapper"}
/// 1: i64 id;
/// }
@scope.Typedef
@scope.Struct
@scope.Field
struct Wrapper {
// The name of a Hack wrapper class used to wrap the field
/// The name of a Hack wrapper class used to wrap the field
1: string name;
// When applied directly to a typedef or struct, the IDL name of the
// type will refer to the adapted type in Hack and the underlying thrift struct will be
// generated in a nested namespace and/or with a different name. By default the type/struct
// will be generated in a nested 'thrift_adapted_types' namespace with the same name,
// but both of these can be changed by setting these fields.
// Empty string enables the nested namespace and uses the IDL name for the struct.
/// When applied directly to a typedef or struct, the IDL name of the
/// type will refer to the adapted type in Hack and the underlying thrift struct will be
/// generated in a nested namespace and/or with a different name. By default the type/struct
/// will be generated in a nested 'thrift_adapted_types' namespace with the same name,
/// but both of these can be changed by setting these fields.
/// Empty string enables the nested namespace and uses the IDL name for the struct.
2: string underlyingName;
3: string extraNamespace = "thrift_adapted_types";
} (thrift.uri = "facebook.com/thrift/annotation/hack/Wrapper")

// An annotation that applies a Hack adapter to types. For example:
// @hack.Adapter{name="\\TimestampAdapter"}
// typedef i64 Timestamp;
//
// struct User {
// 1: Timestamp account_creation_time;
// }
//
// Here the field `account_creation_time` will have type TimestampAdapter::THackType instead of i64.
/// An annotation that applies a Hack adapter to types. For example:
/// @hack.Adapter{name="\\TimestampAdapter"}
/// typedef i64 Timestamp;
///
/// struct User {
/// 1: Timestamp account_creation_time;
/// }
///
/// Here the field `account_creation_time` will have type TimestampAdapter::THackType instead of i64.
///
/// in hack:
/// ```
/// final class TimestampAdapter implements IThriftAdapter {
/// const type TThriftType = int;
/// const type THackType = Time;
/// public static function fromThrift(int $seconds)[]: Time {
/// return Time::fromEpochSeconds($seconds);
/// }
/// public static function toThrift(Time $time): int {
/// return $hack_value->asFullSecondsSinceEpoch();
/// }
/// }
/// ```
/// elsewhere in hack:
/// ```
/// function timeSinceCreated(Document $doc): Duration {
/// // $doc->created_time is of type Time
/// return Duration::between(Time::now(), $doc->created_time);
/// }
/// ```
/// This completely replaces the underlying type of a thrift for a custom implementation and uses
/// the specified adapter to convert to and from the underlying Thrift type during (de)serialization.
@scope.Typedef
@scope.Field
struct Adapter {
// The name of a Hack adapter class that implements IThriftAdapter
/// The name of a Hack adapter class that implements IThriftAdapter
1: string name;
}

Expand All @@ -85,31 +108,69 @@ struct SkipCodegen {
1: string reason;
}

// This annotation is mainly used to rename symbols which can result in symbol
// conflict errors in Hack codegen.
// For ex: reserved keywords in Hack language, symbols with similar names from
// other files in Hack
/// This annotation is mainly used to rename symbols which can result in symbol
/// conflict errors in Hack codegen.
/// For ex: reserved keywords in Hack language, symbols with similar names from
/// other files in Hack
@scope.Definition
struct Name {
1: string name;
2: string reason;
}

// This annotation is for adding Hack attributes to union enums.
/// This annotation is for adding Hack attributes to union enums.
@scope.Union
struct UnionEnumAttributes {
1: list<string> attributes;
}

// This annotation is for using a custom trait for structs.
/// This annotation is for using a custom trait for structs.
@scope.Struct
@scope.Union
@scope.Exception
struct StructTrait {
1: string name;
}

// This annotation is for adding Hack attributes.
/// This annotation is for adding Hack attributes.
/// * Where to use: field or struct type
/// * Value: add attributes like `JSEnum` to structs or fields
/// * Example:

/// ```
/// // In thrift
/// enum MyEnum {
/// ALLOWED = 1,
/// THIS_IS_ALLOWED = 2,
/// THIS_IS_ALLOWED_2 = 3,
/// }(
/// hack.attributes=
/// "\JSEnum(shape('name' => 'MyEnum')),
/// \GraphQLEnum('MyEnum', 'Description for my enum',)"
/// )
/// struct MyThriftStruct {
/// 1: string foo (hack.attributes = "FieldAttribute");
/// 2: string bar;
/// 3: string baz;
/// } (hack.attributes = "ClassAttribute")
/// ```
/// ```
/// //thrift compiler will generate this for you
/// <<\JSEnum(shape('name' => 'MyEnum')),
/// \GraphQLEnum('MyEnum', 'Description for my enum',)>>
/// enum MyEnum: int {
/// ALLOWED = 1;
/// THIS_IS_ALLOWED = 2;
/// THIS_IS_ALLOWED_2 = 3;
/// }
/// <<ClassAttribute>>
/// class MyThriftStruct implements \IThriftStruct {
/// ....
/// <<FieldAttribute>>
/// public string $foo;
/// ....
/// }
/// ```
struct Attributes {
1: list<string> attributes;
}
Expand All @@ -119,7 +180,7 @@ struct Attributes {
@scope.Exception
struct StructAsTrait {}

// This annotation is to generate an entity as internal
/// This annotation is to generate an entity as internal
@scope.Struct
@scope.Union
@scope.Enum
Expand Down
Loading

0 comments on commit 110b603

Please sign in to comment.