GithubHelp home page GithubHelp logo

Comments (5)

ybainier avatar ybainier commented on September 27, 2024

Hi,

although I think it is possible, I also think it would break the idea of having a single point of configuration per app. The purpose of the container is just to spare you the cost of wiring your stuff by yourself.

Why is the shared_ptr reference counter overhead so costly to you? In what particular case do you have to let the container build your app's components and then move the components around?

I can either try to understand and help you out or I can suggest you to have a look to another IOC container which supports what you are asking for (but it is a static one).

Tell me

from hypodermic.

osadowskyphd avatar osadowskyphd commented on September 27, 2024

Hi again,

What concerns me with shared_ptr is less the performance or memory overhead and more the question of ownership and destruction time. When there is shared ownership, one can never be sure when the object in use will be destructed.

In my use case, I have a family of classes that optionally may use some service. The service is represented as another class (let's call it "Service"), and each instance in the family is initialized with its own instance of Service, which may be null or injected from outside. The expected behavior is that the Service instance will be destructed with the containing class.

class Service;

class FamilyBase
{
public:
    // some_ptr may be shared or unique
    FamilyBase(some_ptr<Service> && servicePtr) : mServicePtr(std::move(servicePtr)) { }
private:
    some_ptr<Service> mServicePtr;
};

While Hypodermic can inject a shared_ptr with use count of 1 to the FamilyBase constructor, there is no built-in constraint against injection of a shared_ptr with a larger reference count. For example, if I decide to inject the dependency manually, I can inject a shared_ptr with any reference count.

On the other hand, a unique_ptr ensures a binary state of the "reference count", and I can be sure that the Service instance will, indeed, be destructed when FamilyBase is destructed.

As to your suggestion to look at Boost.DI - our group has examined it in the past. At the time, it did not support dynamic runtime registration of new types, which was a critical feature for our project. We communicated with the author, who said that he added such a feature following our request. It's indeed very kind, but we did not want to take the risk of using a completely fresh feature, so we fell back to Hypodermic.

With this said, I wanted to raise another question about resolving non-shared_ptr values. Specifically, I want to have a class whose constructor takes some parameters from Hypodermic and other parameters from the caller. I don't expect the current resolve method to address this. But I wonder if it's possible to design some generic form for this operation.

Here's an example, somewhat artificial and contrived, but shows the direction.

class Logger
{
public:
    Logger(std::string const & name);
};

void foo()
{
    // define builder, container, etc.
   // now I want to resolve<Logger> and somehow pass the name in the same call/line.
}

I know that it's possible to create a nested container where a specific instance is registered as string and resolve from that container. However:

  1. It is quite inconvenient syntactically to go through all this more-or-less boilerplate stuff just to get a string in the end.
  2. When I get the string, I can only get it as shared_ptr, which is yet another unnecessary overhead.
  3. Worse of all, if there are several such parameters, I must register all of them, and if the same type repeats more than once, I must use an ever more complicated form of register just to get to something essentially simple.

Let's make the example a little more interesting to enhance the discussion.

class LoggerDependency;

class Logger
{
public:
    Logger(std::shared_ptr<LoggerDependency> dependency, std::string const & name);
};

What I am after is some "construct" that would take a string in this case and do the following imaginary operation.

std::shared_ptr<Logger> myLoggerCreator(std::string const & name)
{
    // imagine that container is defined somewhere
    auto dependency = container.resolve<LoggerDependency>();
    return std::make_shared<Logger>(std::move(dependency), name);
}

But I want this in generic form, so I would not have to manually write this for each resolved product and each parameter pack.

Can you think of anything in this direction?

from hypodermic.

ybainier avatar ybainier commented on September 27, 2024

I understand you would like the compiler to keep ensuring —statically— the lifetime of your components. I don't know if or when I will rewrite a proper storage so that the container could resolve some unique pointers. For now, you have to configure your components and kind of let the container manage the resources, i.e., single instances will be released when they are no longer used by your app or by the container.

Regarding the injection of basic types instances, you have to provide some factories. I would write something like this:

template <class TProduct>
std::shared_ptr< TProduct > buildProduct(std::string const & name)
{
    // resolve something that is either registered or autowirable/registerable on the fly
    auto product = container.resolve< TProduct >();
    product->configureName(name);
    return product;
}

Is this close to what you are looking for?

from hypodermic.

osadowskyphd avatar osadowskyphd commented on September 27, 2024

Hi Yohan,

I understand the issue with resolution of unique_ptr. For now, we can stay as it is, but I thought it was at least worth mentioning.

With regards to the basic types, I first want to emphasize that I don't want a configureName method but to pass the name somehow as part of the construction of the product. As I wrote above, we currently address this by creating a nested container in which the specific values of the basic types are registered, as well as a factory for TProduct which resolves those arguments by name. It is cumbersome and not very efficient, but it kind of works. Efficiency aside, what I have in mind is something a little like the std::bind syntax (and I really dislike std::bind!), which may be a little similar to this:

class Product {
public:
  Product(std::shared_ptr<Service> servicePtr, std::string const & name);
};

// here we set up a container that knows how to resolve for Service, at least.

std::shared_ptr<Product> = myResolve<Product>(container, /* maybe some placeholder designation, */
                                                                                 "productName");

where myResolve takes care of all the mechanics of building the nested container, and finally resolves from it.

I must admit that I always become confused when it comes to the actual semantics of std::placeholders used in bind, and I wish that there was a friendlier way to express the true intention of the caller. But for lack of a better tool, I'd use it if it solved the issue.

I do think that it is possible to define a generic function like myResolve, and, if you had wished, make it part of the library. But here I am just interested in your opinion about the feasibility of such a function and maybe if you have an idea how it can be written.

Regards,

Ofri

from hypodermic.

ybainier avatar ybainier commented on September 27, 2024

Hey Ofri,

Hypodermic statically knows what your constructor looks like by the time you are registering its type. When you build the container, it is described to a kind of type erased structure. What you are asking for (if I get it right) is currying and I don't know how to mix up constructor arguments deduction with partial function so that you can fill the gaps later dynamically. This is way easier when everything is static but a static container is template contagious. Again, I may find a way by rewriting the storage but for now, I have no idea to add this feature.

Although I agree that using a nested container for this peculiar use reveals to be cumbersome, mind if I ask what is so wrong about invoking a configureName method by the time you let the container create your whole component in a factory and then setup some stuff before returning your properly initialized fresh instance? No nested container, just a new method to expose.

from hypodermic.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.