Ok I see, generated headers indeed include capnproto/kj headers, so Cap'n Proto would be a public dependency of my library. The version mismatch issue could be solved by using a CMake package, with the right version of Cap'n Proto required in the CMake configuration file. Based on all this, I decided that Cap'nProto should stay a private dependency (I'm building it inside my project with* CPM.cmake <https://github.com/cpm-cmake/CPM.cmake>*), even if I need to static_cast my enums on both sides.
Thank you again for your time ! Le mercredi 28 avril 2021 à 21:26:46 UTC+2, [email protected] a écrit : > I would say that when you are distributing a precompiled binary copy of > your library, you should distribute the `.capnp.h` header along with it, if > you want programs using your library to be able to use that type. Those > programs will need to link against the exact version of the schema that you > compiled into your library, so you might as well provide them with the > matching header. Otherwise, if they try to generate their own copy, they > may run into linker errors due to duplicate symbols -- or worse, subtle > runtime ODR errors if their version didn't exactly match what's in your > library. > > With all that said, there's a second layer of this problem, which is that > your library and applications using it need to link against exactly the > same version of Cap'n Proto. Unfortunately, different versions of the Cap'n > Proto library are not ABI-compatible, and headers generated for one version > won't necessarily work with a different version of the library. If you > don't want your library to impose a specific version of Cap'n Proto on the > applications linking with it, then either you need to let each app compile > your library themselves (using the version of capnp they like), or you need > to do symbol-renaming stuff in order to embed a private copy of capnp into > your binary. In the latter case, you can't really distribute the .capnp.h > header since it would not work with any version of Cap'n Proto other than > your private one. > > -Kenton > > On Tue, Apr 27, 2021 at 2:36 AM Théophile B. <[email protected]> wrote: > >> It works ! Thank you for the expanded explanation, it helped a lot. I >> initially though there would be a "prettier" out-of-the-box solution, but as >> long as it works... >> >> While I have you, I have a more general question. Is it common case to >> distribute Cap'n'Proto genererated headers as part of a library, or >> everything must stay private ? >> Let's say I have defined some enum in the .capnp and I want the users of >> my library to use them. >> For the moment I have a duplicated the definition of this enum in a >> public header, and I do static casts before serialization. >> Le lundi 26 avril 2021 à 17:40:59 UTC+2, [email protected] a écrit : >> >>> You need your `SubscriberImpl` to hold a regular C++ pointer back to the >>> `MediaLibraryImpl` that created it, so that you can access its >>> `publishers_` set through that pointer. You can pass the pointer to >>> `SubscriberImpl`'s constructor. >>> >>> You'll also want to make sure the pointer doesn't become a dangling >>> pointer, so `SubscriberImpl` should also hold a `MediaLibrary::Client` >>> which points to the same `MediaLibraryImpl` -- holding this counts as a >>> strong reference so Cap'n Proto won't destroy the `MediaLibraryImpl`. >>> Inside `addSubscriber()` you can use `thisCap()` to get a >>> `MediaLibrary::Client` pointing to `this`, then you'd pass that to >>> `SubscriberImpl`'s constructor, along with the plain C++ pointer to `this`. >>> >>> -Kenton >>> >>> On Mon, Apr 26, 2021 at 10:25 AM Théophile B. <[email protected]> >>> wrote: >>> >>>> Thank you for your quick answer! >>>> >>>> The `Server` to `Client` "conversion" is done via the bootstrap >>>> capability, but I need the underlying `Server` back when calling >>>> capabilities returned by the bootstrap capability. >>>> Here is a simplified example where I (tried) to implement your advice: >>>> >>>> interface MediaLibrary { >>>> addPublisher @0 (name :Text) -> (pub :Publisher); >>>> addSubscriber @1 (name :Text) -> (sub :Subscriber); >>>> >>>> interface Publisher {} >>>> >>>> interface Subscriber { >>>> listenTo @0 (publisher :Publisher); >>>> } >>>> } >>>> >>>> class MediaLibraryImpl final : public MediaLibrary::Server { >>>> public: >>>> kj::Promise<void> addPublisher(AddPublisherContext context) override { >>>> my_underlying_library::Publisher* output = ...; // call the >>>> underlying lib >>>> MediaLibrary::Publisher::Client pub = publishers_.add(kj::heap< >>>> PublisherImpl>(output)); >>>> context.getResults().setPub(pub); >>>> return kj::READY_NOW; >>>> } >>>> >>>> kj::Promise<void> addSubscriber(AddSubscriberContext context) override >>>> { >>>> my_underlying_library::Subscriber* output = ...; // call the >>>> underlying lib >>>> MediaLibrary::Subscriber::Client sub = subscribers_.add(kj::heap< >>>> SubscriberImpl>(output)); >>>> context.getResults().setSub(sub); >>>> return kj::READY_NOW; >>>> } >>>> >>>> private: >>>> capnp::CapabilityServerSet<MediaLibrary::Subscriber> subscribers_; >>>> capnp::CapabilityServerSet<MediaLibrary::Publisher> publishers_; >>>> }; >>>> >>>> class PublisherImpl : public virtual GstDaemon::Publisher::Server { >>>> protected: >>>> PublisherImpl(my_underlying_library::Publisher* pub) : pub_(pub) {} >>>> >>>> private: >>>> my_underlying_library::Publisher* pub_; >>>> } >>>> >>>> class SubscriberImpl : public virtual GstDaemon::Subscriber::Server { >>>> protected: >>>> SubscriberImpl(my_underlying_library::Subscriber* sub) : sub_(sub) {} >>>> >>>> kj::Promise<void> listenTo(ListenToContext context) override { >>>> auto pub = context.getParams().getPublisher(); // GstDaemon::Publisher >>>> ::Client >>>> // No access to CapabilityServerSet ! >>>> // We we would like to do: sub_->listenTo("pub_") >>>> } >>>> >>>> private: >>>> my_underlying_library::Subscriber* sub_; >>>> } >>>> >>>> If I understand well, I can now do subscribers_.getLocalServer(" >>>> GstDaemon::Publisher::Client") from `MediaLibraryImpl`. >>>> Bu what about SubscriberImpl, where the `listenTo` needs `PublisherImpl` >>>> ? >>>> Is it possible to have access to `MediaLibraryImpl` from ` >>>> SubscriberImpl` ? >>>> Instead, should I pass references to the `CapabilityServerSet` to ` >>>> PublisherImpl` and `SubscriberImpl` ? >>>> >>>> Le lundi 26 avril 2021 à 16:30:55 UTC+2, [email protected] a >>>> écrit : >>>> >>>>> Hi Théophile, >>>>> >>>>> Take a look at CapabilityServerSet: >>>>> >>>>> >>>>> https://github.com/capnproto/capnproto/blob/6b5bcc2c6e954bc6e167ac581eb628e5a462a469/c++/src/capnp/capability.h#L607-L633 >>>>> >>>>> When you convert your `Server` object to a `Client`, you need to do it >>>>> using a CapabilityServerSet. Later, you can use the same set to unwrap >>>>> the >>>>> `Client` and get the underlying `Server` back -- even if the client has >>>>> been passed over the network and back in the meantime. >>>>> >>>>> -Kenton >>>>> >>>>> On Mon, Apr 26, 2021 at 9:25 AM Théophile B. <[email protected]> >>>>> wrote: >>>>> >>>>>> I'm not sure I'm usign the right terminology in my question, but I >>>>>> hope you will understand my problem with the following example. >>>>>> >>>>>> I'm developing a media library where the user can play/pause/stop >>>>>> different kinds of "pipelines" (source ~ camera, filter ~ processing, >>>>>> sink >>>>>> ~ display). Depending on their type, these pipeline can listen to each >>>>>> other, or not (e.g: a source pipeline cannot listen). I'm now working on >>>>>> a >>>>>> "capnp wrapper" of the library, so that it can be controlled via RPC. >>>>>> >>>>>> I have replicated the class inheritance with success: >>>>>> >>>>>> interface Publisher {} >>>>>> >>>>>> interface Subscriber { >>>>>> listenTo @0 (publisher :Publisher); >>>>>> } >>>>>> >>>>>> interface Pipeline { >>>>>> play @0 (); >>>>>> pause @1 (); >>>>>> stop @2 (); >>>>>> } >>>>>> >>>>>> interface SourcePipeline extends(Pipeline, Publisher) {} >>>>>> >>>>>> interface FilterPipeline extends(Pipeline, Publisher, Subscriber) {} >>>>>> >>>>>> interface SinkPipeline extends(Pipeline, Subscriber) {} >>>>>> >>>>>> Server-side I have those impl classes, which holds the references to >>>>>> the underlying objects of the media library (pub_ & sub_). >>>>>> >>>>>> class PublisherImpl : public virtual GstDaemon::Publisher::Server { >>>>>> protected: >>>>>> PublisherImpl(my_underlying_library::Publisher* pub) : pub_(pub) {} >>>>>> private: >>>>>> my_underlying_library::Publisher* pub_; >>>>>> }; >>>>>> >>>>>> class SubscriberImpl : public virtual GstDaemon::Subscriber::Server { >>>>>> protected: >>>>>> SubscriberImpl(my_underlying_library::Subscriber* sub) : sub_(sub) {} >>>>>> >>>>>> kj::Promise<void> listenTo(ListenToContext context) override { >>>>>> auto pub = context.getParams().getPublisher(); // GstDaemon:: >>>>>> Publisher::Client >>>>>> // ??? = PublisherImpl::pub_ >>>>>> sub_->listenTo(???) >>>>>> } >>>>>> >>>>>> private: >>>>>> my_underlying_library::Subscriber* sub_; >>>>>> }; >>>>>> >>>>>> Now, from the client, I wanted to do something like this: >>>>>> //GstDaemon::Subscriber::Client subscriber; // already returned by >>>>>> the server >>>>>> //GstDaemon::Publisher::Client publisher; // already returned by the >>>>>> server >>>>>> auto request = subscriber.listenToRequest(); >>>>>> request.setPublisher(publisher); >>>>>> auto promise = request.send(); >>>>>> >>>>>> However, as you can see, on the server it's not possible to get the pub_ >>>>>> pointer when the listenTo method is called. Is there any way I can >>>>>> get a reference to the PublisherImpl here ? >>>>>> >>>>>> -- >>>>>> You received this message because you are subscribed to the Google >>>>>> Groups "Cap'n Proto" group. >>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>> send an email to [email protected]. >>>>>> To view this discussion on the web visit >>>>>> https://groups.google.com/d/msgid/capnproto/488a63c6-92bc-4bc8-9ab8-e648d3a577bfn%40googlegroups.com >>>>>> >>>>>> <https://groups.google.com/d/msgid/capnproto/488a63c6-92bc-4bc8-9ab8-e648d3a577bfn%40googlegroups.com?utm_medium=email&utm_source=footer> >>>>>> . >>>>>> >>>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "Cap'n Proto" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to [email protected]. >>>> >>> To view this discussion on the web visit >>>> https://groups.google.com/d/msgid/capnproto/45982dae-5fb0-4adb-8b15-9fea70506d25n%40googlegroups.com >>>> >>>> <https://groups.google.com/d/msgid/capnproto/45982dae-5fb0-4adb-8b15-9fea70506d25n%40googlegroups.com?utm_medium=email&utm_source=footer> >>>> . >>>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "Cap'n Proto" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> > To view this discussion on the web visit >> https://groups.google.com/d/msgid/capnproto/8c279b5e-0fe0-4dac-af58-22eeda468c92n%40googlegroups.com >> >> <https://groups.google.com/d/msgid/capnproto/8c279b5e-0fe0-4dac-af58-22eeda468c92n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- You received this message because you are subscribed to the Google Groups "Cap'n Proto" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/capnproto/8bc2e828-3128-4c01-a419-9cb65cf1877en%40googlegroups.com.
