-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plans for cppgraphqlgen 5.0 #311
Comments
I'm planning on making a release candidate as soon as the CI build finishes. I haven't done any of the documentation updates yet, but if anyone wants to take it for a spin, 5.0.0 should be completely implemented in the next branch now. |
Sorry,I am not qualified to demand anything, but let me share my personal feelings, We compared cpp ,Golang and Rust, ,their(rust,Golang ) grphql framework makes it easier for a novice to start from scratch. Would you consider generating something similar, This is an example of Rust, which really makes it very easy for a beginner to get started, rust-graphql-example The CPP framework requires us to write some checks ourselves, which is very long and difficult for beginners. In fact, many times we don't need to know the underlying details, we just want to write business logic |
There are some of those samples out there for To be honest, I prefer working in Rust with async-graphql myself. In a situation where I'm just trying to implement a GraphQL service with the performance of a compiled native language, I'd reach for that first. What The
I don't recommend using the |
Thank you for your reply, Actually, our project is 100% cpp, so I would prefer cpp to have a graphql framework similar to rust‘s async-graphql, But in reality, there is no decent GraphQL framework in the CPP , Therefore, we ultimately chose to use Golang to implement the GraphQL server, But personally, But I sincerely hope that CPP also has a decent(simple + easy-to-use) GraphQL framework |
Yeah, unfortunately C++ just can't do it as simply. I haven't used Golang or its GraphQL frameworks, but I am familiar with a couple of Rust GraphQL frameworks, and a lot of their ease-of-use comes from procedural macros. There are a few proposals out there for static reflection in C++ or meta-classes that could do something similar, but AFAIK those are many years away from becoming part of the standard. I think static code generators are the closest we can get to hiding the ugly details from C++. |
In fact, cpp can generate code like Golang and finally expose something similar to a callback function. We only need to implement business logic in the callback function |
Actually, I think cppgraphqlgen should be able to be packaged into something like this, But it may be necessary to write a code generation tool to generate the graphql_server class
|
I have been using cppgraphqlgen in production code for about 2 years. Here are some of my thouths:
Extension schema
I my opinion it should be possible to generate two separate compilation units/libraries that could be literally added to each other at runtime. As in:
Addition should expand
A. Response is held in B. There should be more fine grained control for subscriptions (especially NotifySubscribe and NotifyUnsubscribe). I think subscription should return a handle to allow external fine grained control. C. The library always wraps objects and scalars in awaitable future (and then awaits them) even when result is returned by value. This causes ridiculous overhead when creating scalar arrays. So far i haven't found a use case for
I agree. And also since all fields originate from schema there will always be a need for external tool to parse schema and generate fields. Static reflection can maybe used to reduce amount of generated code. I don't think there will ever be a compile time AST parser that does not kill the compiler. EDIT: |
What you're describing sounds more like a web framework than what |
Great feedback below, thanks!
Interesting, yeah. I'm not sure if having schema extensions be handled differently makes sense, but given 2 separate schemas, it should be possible to merge them at the schema level. The only tricky bit, I think, would be figuring out how to dispatch the resolvers to the correct implementation in the merged service at runtime. It might be better to treat this as a new type of service that multiplexes other static services dynamically, including building a merged schema for validation and introspection.
As far as converting to JSON goes, there is a visitor pattern already when starting from a
I think you're saying that the set of resources/listeners you are tracking to support the subscription might change dynamically, e.g., as items are added or removed from a collection. The approach I'd use for that would probably be to use external bookkeeping for those resources as you do now, but store a handle/ID to those external resources in the
One of the changes in the next branch, fa15fb7, had a surprisingly big impact on the I haven't checked a release build yet, but I'd be curious to know if this improves your scalar array scenario.
I'll have to dig into this some more, but I gather you specialize the
Re: splitting the enums and input types into one or more separate headers, I figured I'd keep it simple for now and just include the |
We actually need an easy-to-use graphql service, Both rust and golang are easy to integrate with http servers,What I mean is that cppgraphqlgen doesn't easily fit into the cppweb framework,If this project could be like rust aync-graphql, I think it would be more popular |
I think it is called Schema stitching. It seems like easiest route for runtime extensibility. It does not cover type extension but it can be emulated by replacing different static services dynamically.
yes and I reckon the response::Value building part can also be redesigned into visitor pattern.
Exactly!
And the writer would build response::Value or JSON or even act directly on visited values depending on user provider visitor.
I think it all boils down to proper SAX style writer API
I will definitely check this out. With my custom changes i did nou have time to port them to newer library verion
No there is no specialiation of The
Field accessor returns
I purposely decided to use external GraphQLUnion trait class instead of creating mapping inside TypeA/TypeB classes to be able to compile all user code without including any of generated class headers. That gives me the ability to generate multiple different schemas, reuse the objects and even adjust union mappings where necessary. At this moment my build flow looks like this:
This solution would work nicely if only there we something equivalent to It does not fully give extensibility but it is somewhere in between.
I am including it. The problem is it pulls all other headers from cpp file. |
I very much like the work you did here. I need to migrate to next branch ASAP and test the changes in my use case. I also like the new stitching ability. Can the same approach can be used to stitch nested fields? It now looks like only query/mutation/subscription can be stitched. |
Thanks! I'm planning on doing a pilot migration of https://github.com/microsoft/gqlmapi as well, and that might flush out some issues. Having more coverage with your project will help avoid needing to make another version right after the release.
Using an empty
There are 2 JSON style visitors now, and I'm considering collapsing them into one, using the At that point, I'll probably also add a
I haven't built anything that does this yet, but it should be possible. The resolver that returns one of the type-erased It requires modification of one of the sub-schema's resolvers too make it aware of this, so all I've done so far is to use the preexisting mock BTW, in case of overlaps, the first schema (the one where you call |
One more caveat about schema stitching: since the include guards will collide on types with the same name, the cleanest/simplest way to handle that is probably to import the generated C++20 modules instead of including the headers when creating the partial When mixing includes and headers, always do all the includes first and then switch to imports in the main (i.e., |
I think relying on modules and imposing strict including rules will limit portability and ease of use. Can't we just add namespace to include guard? And maybe also to filenames to prevent confusing filename duplicates? Then only requirement for stitching will be to use different namespaces for stitching parts. |
With stitching I see 3 fields that need individual care:
|
Since the type-erased objects are all inherited from The tricky part with nested resolvers is, where do you get the instance of a type-erased object from the other schema? Root objects are easy, they're fixed at service instantiation, but there's a path of resolvers (some of which may not exist in both schemas) leading to the returned object in a nested resolver. That's what I think blocks auto-stitching, e.g., somewhere in
Type extensions are parsed the same way as regular type declarations. Technically, it probably shouldn't accept an extension without a base declaration somewhere, but I don't remember if I ever tried that. So, if you want to use the
The way it's meant to work currently is that the schema will merge the enum values, so it'll pass validation. When you call a resolver in either schema with the same enum type, it'll pass it through the Aside from introspection queries against the stitched sample, I haven't fully tested this, so LMK if you're seeing it break. |
I was thinking some kind of extensible ObjectModel Factory with tag dispatch. Pseudocode sketch: Base schema:
Extend schema:
Generated from concatenation of all schema parts:
Generated for Base:
Generated for Extend:
User implementation:
An then add some template template magic:
The idea requires more work, but I think it shows the idea. Also I think tag dispatch would also elegantly solve Unions and their extension - user would return a std::variant<std::pair<TypeTag,std::shared_ptr<Object_t>,...> and resolver would Create adequate model. I am aware that following such an approach would be total table flip and library overhaul, but in my opinion It would substantially expand its capabilities. User could even overload StitchedFactory::Create on per Type basis and perform additional runtime logic when needed. Tell me what you think. If you find potential in such approach I am very willing to put some more work into it. For backwards compatibility ModelFactory could also have specialization for type erased Object.
This what I am doing right now. Take example above. Given an "extension schema part" I create dll for Base schema and one dll for Base+Extend schema. Then at runtime I select desired dll and use it as my resolver. This however does not scale as I need to create every possible combination with multiple "extension schema part"s. Also to achieve that I had to modify the library to move all generated types into dlls - "user" code does not instantiate |
I finally got some time to dig into my performance issues. Again great work with
I run Unmodified convert
Shortcut for std::vector of non-awaitable scalars.
Convert speedup
The same shortcut could be implemented for multidimensional arrays. Probably the code could be simplified if there were some kind of ValueToken traits. Next I will try to migrate my project to next branch. |
I have concluded my transition to next branch. I am vary happy with the result. SharedTypes and value visitor simplified everything sybstantially. There are few additional changes that could be merged if you think they could be useful in general. Please have a look at: |
This is where I'm going to start tracking features for 5.0, the next major version of
cppgraphqlgen
. This will be a breaking change for existing consumers, so it's an opportunity to cleanup deprecated methods and simplify some of the APIs. I'm also going to adopt a few more C++20 and C++23 features if they are well enough supported on Windows, Linux, and macOS now.This is using #172, the tracking issue for
cppgraphqlgen
4.0, as a template and starting point.std::fmt
instead ofstd::ostringstream
.cppgraphqlgen
Awaitable
types.clientgen
andschemagen
: See Cannot use multiple client object in single compilation unit. #264 for more discussion.JSONResponse.*
with other JSON libraries besidesRapidJSON
.service::ResolverResult
. #320...Schema.h
instead of inline declarations. #321I'll update this list as new features crop up and as I/we make progress on it. If there's something you'd like to see that I haven't included already, feel free to comment on this issue.
The text was updated successfully, but these errors were encountered: