|
| 1 | +# Migration Guide for v4.x |
| 2 | + |
| 3 | +Most of the changes in v4.x affect services generated with `schemagen` more than clients generated with `clientgen`. |
| 4 | + |
| 5 | +## Adopting C++20 |
| 6 | + |
| 7 | +This version takes advantage of and requires the following C++20 features: |
| 8 | + |
| 9 | +- Coroutines (in either the `std` or `std::experimental` namespace) |
| 10 | +- Concepts |
| 11 | + |
| 12 | +There is enough support for these features in the following compiler versions, your mileage may vary with anything older than these: |
| 13 | + |
| 14 | +- Microsoft Windows: Visual Studio 2019 |
| 15 | +- Linux: Ubuntu 20.04 LTS with gcc 10.3.0 |
| 16 | +- macOS: 11 (Big Sur) with AppleClang 13.0.0. |
| 17 | + |
| 18 | +## Using Coroutines |
| 19 | + |
| 20 | +The template methods generated by `schemagen` will construct the expected [awaitable](awaitable.md) type using the result of your field getter methods. If you want to implement your field getter as a coroutine, you can explicitly return the expected awaitable type, otherwise return any type that is implicitly convertible to the awaitable-wrapped type. |
| 21 | + |
| 22 | +Take a look at `Query::getNode` in [samples/today/TodayMock.cpp](../samples/today/TodayMock.cpp) for an example. The `auto operator co_await(std::chrono::duration<_Rep, _Period> delay)` operator overload in the same file is also an example of how you can integrate custom awaitables in your field getters. |
| 23 | + |
| 24 | +## Type Erasure Instead of Inheritance |
| 25 | + |
| 26 | +Change your class declarations so that they no longer inherit from the generated `object` namespace classes. If you need the `shared_from_this()` method, you can replace that with `std::enable_shared_from_this<T>`. Remove the `override` or `final` keyword from any virtual field getter method declarations which were inherited from the object type. |
| 27 | + |
| 28 | +In cases where the return type has changed from `std::shared_ptr<service::Object>` to `std::shared_ptr<object::Interface>` or `std::shared_ptr<object::Union>`, wrap the concrete type in `std::make_shared<T>(...)` for the polymorphic type and return that. |
| 29 | + |
| 30 | +If your implementation is tightly coupled with the object hierarchy from the schema, there are some strategies for separating them discussed in [#210](https://github.com/microsoft/cppgraphqlgen/issues/210). |
| 31 | + |
| 32 | +## Simplify Field Accessor Signatures |
| 33 | + |
| 34 | +Examine the generated object types and determine what the expected return type is from each field getter method. Replace the `service::FieldResult` wrapped type with the expected return type (possibly including the awaitable wrapper). |
| 35 | + |
| 36 | +Make methods `const` or non-`const` as appropriate. The `const` type erased object has a `const std::shared_ptr<T>` to your type, but the type inside of the `std::shared_ptr` is not `const`, so it can call non-`const` methods on your type. You can get rid of a lot of `mutable` fields or `const_cast` calls by doing this and make your type `const` correct. |
| 37 | + |
| 38 | +Parameters can be passed as a `const&` reference, a `&&` r-value reference, or by value. The generated template methods will forward an r-value reference which will be implicitly converted into any of these types when calling your method. |
| 39 | + |
| 40 | +Remove any unused `service::FieldParams` arguments. If your method does not take that as the first parameter, the generated template method will drop it and pass the rest of the expected arguments to your method. |
| 41 | + |
| 42 | +## CMake Changes |
| 43 | + |
| 44 | +By default, earlier versions of `schemagen` would generate a single header and a single source file for the entire schema, including the declaration and definition of all of the object types. For any significantly complex schema, this source file could get very big. Even the `Today` sample schema was large enough to require a special `/bigobj` flag when compiling with `MSVC`. It also made incremental builds take much longer if you only added/removed/modified a few types, because the entire schema needed to be recompiled. |
| 45 | + |
| 46 | +For a long time, `schemagen` also supported a `--separate-files` flag which would output a separate header and source file for each object type in the schema. This requires more complicated build logic since the set of files that need to be built can change based on the schema, but the end result is much easier to read and incremental builds are faster. |
| 47 | + |
| 48 | +In v4.x, the separate files option is not only the default, it's the only option. Supporting both modes of code generation would have added too much complexity and too many tradeoffs for the simplified build logic. Instead, v4.x adds several CMake helper functions in [cmake/cppgraphqlgen-functions.cmake](../cmake/cppgraphqlgen-functions.cmake) which encapsulate the best practices for regenerating and building the schema targets dynamically when the schema file changes. These functions are automatically included by `find_package(cppgraphqlgen)`. |
| 49 | + |
| 50 | +Replace custom CMake logic to invoke `schemagen` and `clientgen` with the helper functions: |
| 51 | +- `update_graphql_schema_files`: Runs `schemagen` with required parameters and additional optional parameters. The output is generated in the CMake build directory, compared against the contents of the source directory, and any changed/added files will be copied over to the sources directory. |
| 52 | +- `add_graphql_schema_target`: Declares a library target for the specified schema which depends on the output of `update_graphql_schema_files` and automatically links all of the shared library dependencies needed for a service. |
| 53 | +- `update_graphql_client_files`: Runs `clientgen` with required parameters and additional optional parameters. The output is generated in the CMake build directory, compared against the contents of the source directory, and any changed/added files will be copied over to the sources directory. |
| 54 | +- `add_graphql_client_target`: Declares a library target for the specified client which depends on the output of `update_graphql_client_files` and automatically links all of the shared library dependencies needed for a client. |
| 55 | + |
| 56 | +_IMPORTANT_: The `update_graphql_schema_files` and `update_graphql_client_files` functions expect to generate sources in a separate sub-directory from any other source code. To handle removing or renaming generated files, they may delete stale files from the source directory. Just in case, it's a good idea to make sure you have your source code backed up or under source control (e.g. committed in a git repository) before invoking these CMake functions. |
| 57 | + |
| 58 | +With all of the refactoring in v4.x, there ceased to be any separation between the `graphqlintrospection` and `graphqlservice` libraries. Even if you use the `--no-introspection` flag with `schemagen`, the generated code still depends on the general schema types which remained in `graphqlintrospection` to perform query validation. As part of the v4.x release, the 2 libraries were combined back into a single `graphqlservice` target. |
0 commit comments