Jump to content
  • Advertisement
Sign in to follow this  
dmatter

Unity Dependency Injection in an Initialiser List

This topic is 3301 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a situation where there are RAII wrapper components, B and C, that have a dependency on another, shared, RAII wrapper, A. For some reason I have a specific use of these components in mind for which I only require to create all these objects at once and destroy them at once (a more complex use might see these objects having different lifetimes). As such I would like to write a higher level component that encapsulates these components and provides an interface to express this specific use. In order to function it will need to inject an A into a B and a C. If this specific use only required the components to exist for a one-off operation then we might write a function like so:
void do_something_useful(data info)
{
    // all constructed here
    A a(info);
    B b(a);
    C c(a);
    
    // do something with them
    impl(b, c);

    // all destructed here
}
We're quite used to seeing code like this, the function controls their lifetime and forwards the work off. However it is not suitable for my purpose, the objects are stateful (Hmm, state-full? ... Full-of-state [smile]) and need to persist between calls in order for this operation to work properly. The next most obvious solution, therefore, is to use a class, D, and have its ctor/dtor control their lifetime and a member function to perform the operation:
class D
{
    A a;
    B b;
    C c;

public:
    D(data info) : a(info), b(a), c(a) { }
    /* ~D() - no need for an explicit destructor */

    void do_something_useful();
};
This looks fine and it works fine, but it has a subtle nuance, it relies on the order of construction of members in a class to operate correctly. This is well defined, it's the order they appear in the class definition (not the order they appear in the initialisation list!). This, to me, feels slightly wrong for some reason. Maybe it's my distrust of other people not knowing their construction rules or maybe it's because, generally, I/we don't usually expect that the order of data members actually matters in the slightest. I have tried to do some searching to find other examples and opinions of this, the best I have is a post by Antheus where he claims that such designs evolve naturally in C++ as consequence of applying the IOC pattern. My question is, how do other people feel about this? Thanks.

Share this post


Link to post
Share on other sites
Advertisement
I can't recall any specific case where I would depend on initialization order. In most cases I've encountered the dependency was always external.

It's just an issue with usual C++ complexity, at least the order is guaranteed.

Why not just change D to take A as constructor, whereas A is constructed from info?

If state matters, then copying might be an issue anyway. If it doesn't, then A could be implicitly constructed from info. And if there are other life-cycle issues, then it doesn't make sense to force such designs, and just allocate them differently.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Why not just change D to take A as constructor, whereas A is constructed from info?
Certainly, if we assume the proposal in my post is bad, then alternatives are fairly easy to produce:
D make_d(data info)
{
return D(A(info));
}
This seems like a likely route.

I could also use pointer types to defer construction to D's constructor body and do it all in there explicitly, but on the heap. In theory it could be the case that B and C are default constructable and fast-swappable/assignable, although they're not here I'm saying.

Share this post


Link to post
Share on other sites
I just don't see what the big deal is. Do people you work with usually randomly reorder members in classes for no reason?

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
I just don't see what the big deal is. Do people you work with usually randomly reorder members in classes for no reason?


Unlike passing 'this', the examples above do not generate any warnings by the compiler (at least MSVC doesn't). Change the order for whichever, and dependent objects suddenly get garbage.

I've only needed this type of dependency on a handful of very top-level objects, where order is somewhat obvious, and members don't really change.

Elsewhere, I'd simply pass info to A, B and C. That avoids the problem altogether. As always, if the shoe doesn't fit...

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Change the order for whichever, and dependent objects suddenly get garbage.

Yes, I understand the technical part of the OP's question. That doesn't address what I asked: do people you work with usually randomly reorder members in classes for no reason?

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Unlike passing 'this', the examples above do not generate any warnings by the compiler (at least MSVC doesn't). Change the order for whichever, and dependent objects suddenly get garbage.

G++ will although at the present moment I can not remember which flag gives the warning :)

Share this post


Link to post
Share on other sites
Quote:
Original post by magic_man
Quote:
Original post by Antheus
Unlike passing 'this', the examples above do not generate any warnings by the compiler (at least MSVC doesn't). Change the order for whichever, and dependent objects suddenly get garbage.

G++ will although at the present moment I can not remember which flag gives the warning :)


struct X {
int x, y;
X () : y(0), x(0) {}
};


main.cc: In constructor `X::X()':
main.cc:2: warning: `X::y' will be initialized after
main.cc:2: warning: `int X::x'
main.cc:3: warning: when initialized here

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!