-
Notifications
You must be signed in to change notification settings - Fork 200
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
Annotated arguments, bindInstance and factories #98
Comments
Also added a note on how to inject annotated values, thanks for the suggestion. You can fix your example code by passing a Side note: not sure why you're using factories, it's a very advanced API that should not be necessary in most cases (is it just for this parameter or is there some other reason to use it?), and adds complexity because now all places that inject that object need to also use factories (which in rare cases is justified, but often not). Also note that annotated and assisted params are orthogonal; you can use annotated params without having to bind a factory, or factories with annotated params. Not sure if this was already clear, I just wanted to make sure it is. Passing parameters to That way the code remains encapsulated: some other test (e.g. an integration test) might not need to override all values that need to be overridden in unit tests. |
I hope that helps. |
I had tried references ( I'm using factories because I'm doing a lot of hardware interfacing and inter-thread communication between application-length threads, and that means many of the same thing in some cases - one example is the binary semaphores from my last issue, multiple of which are used by control threads to stop or start parts of persistent worker threads; another is I2C connections, where there may theoretically be up to 127 instances of the I2CConnection class spread throughout different device manager objects. The bit about annotated parameters in factories was immediately clear, but it took me a bit to figure out how to use them with fruit::Component<std::function<std::unique_ptr<Foo>()>> getFooFactoryComponent()
{
return fruit::createComponent().bind<Foo, FooImpl>().
registerFactory<std::unique_ptr<Foo>(fruit::Annotated<Bar, Baz> qux)>([](Baz* qux) {
return std::unique_ptr<Foo>(new FooImpl(qux));
});
} About fruit::Component<std::function<std::unique_ptr<UsesSomeOtherFile>()>> getUsesSomeOtherFileFactoryComponent()
{
return fruit::createComponent().bind<UsesSomeOtherFile, UsesSomeOtherFileImpl>()
.bindInstance<fruit::Annotated<SomeOtherFile, std::string_view>>(fileConstants::someOtherFile);
} and then override just the |
I meant this, not putting the reference in the return type:
So that Fruit gets a reference to the actual static var you defined somewhere rather than the param (which is a stack variable that is destroyed after the call to
Yes, that's a bit complex. Usually (as in this case AFAICT, unless there are additional complications) you can get away with using the INJECT() wrapper on the constructor instead, so that Fruit generates that boilerplate code for you.
That's true, but it's not really a limitation. You can extract the bindInstance in a separate |
I see, then if you really need separate instances (and especially with so many of them) I agree factories are the way to go for classes like the I2CConnection you describe. I was just warning you since I've seen people go down the "make everything a factory" route and then ending up not being able to do sth that would be simple in the non-factory case. |
Aside from not using I got a bunch of error messages like this:
The same happens when using non-const Which makes sense, I think - the Quick Reference specifies that
Ah. I'd hoped to avoid that due to the multiplication of component definitions, but having experimented with potential alternatives it really is simpler and safer.
I've got a little of a bunch of things - basic injection where I can, factories for things that need either a bunch of instances with a varying parameter or multiple identical instances in different places, annotation for one case where there are exactly two instances of a class and they have different constants (so I have Thanks for the advice! |
Oh, it looks like reference params to
|
Useful to know. Thanks! |
For future reference, filed #99 to track the bug about the compile error with references. |
I may have another one. This: // fileConstants.h
namespace fileConstants
{
struct FooFilePath{};
inline const std::string_view fooFile = "foo.json";
inline fruit::Component<fruit::Annotated<FooFilePath, std::string_view>> getFooFilePathComponent()
{
return fruit::createComponent().bindInstance<fruit::Annotated<FooFilePath, std::string_view>>(fooFile);
}
} gets this:
But this: // fileConstants.h
namespace fileConstants
{
struct FooFilePath{};
inline const std::string_view fooFile = "foo.json";
inline fruit::Component<fruit::Annotated<FooFilePath, const std::string_view&>> getFooFilePathComponent()
{
return fruit::createComponent().bindInstance<fruit::Annotated<FooFilePath, const std::string_view&>>(fooFile);
}
} Gets this:
According to the comment in component.h, both should work. (Line 15 is the line of Trying to bind I originally got this with the 3.4.0 release, but I updated to master and it's still happening. |
You should declare:
fruit::Component<fruit::Annotated<FooFilePath, const std::string_view>>
(even if you're passing a const&)
The argument to fruit::Component/Annotated/Injector should not be a pointer
or reference, you just need to declare if it's const or not.
…On Fri, 21 Feb 2020, 00:58 Trevor Muraro, ***@***.***> wrote:
Reopened #98 <#98>.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#98?email_source=notifications&email_token=AALZ4FY2WGUBES33A7WE5ODRD6JSPA5CNFSM4KY3JH5KYY3PNVWWK3TUL52HS4DFWZEXG43VMVCXMZLOORHG65DJMZUWGYLUNFXW5KTDN5WW2ZLOORPWSZGOWZPZW4Q#event-3059719026>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AALZ4F5TIZ5QG7T7C2GJ2KDRD6JSPANCNFSM4KY3JH5A>
.
|
That also gets NonClassTypeError: // fileConstants.h
namespace fileConstants
{
struct FooFilePath{};
inline const std::string_view fooFile = "foo.json";
inline const fruit::Component<fruit::Annotated<FooFilePath, const std::string_view>> getFooFilePathComponent()
{
return fruit::createComponent().bindInstance<fruit::Annotated<FooFilePath, const std::string_view>>(fooFile);
}
}
|
Ah, never mind - I found the test case that shows the working solution. namespace fileConstants
{
struct FooFilePath{};
inline const std::string_view fooFile = "foo.json";
inline const fruit::Component<fruit::Annotated<FooFilePath, const std::string_view>> getFooFilePathComponent()
{
return fruit::createComponent().bindInstance<fruit::Annotated<FooFilePath, std::string_view>>(fooFile);
}
} |
I have started using parameterization, annotation and
bindInstance
to pass global constants to my objects, as such:This allows UsesSomeFile to be created with the hardcoded file path for the application, but to be given a different file path in tests. However, when I tried to do the same for a factory-generated class:
The program compiles, but throws
std::bad_alloc
on attempting to installgetUsesSomeOtherFileFactoryComponent
.I couldn't find anything on using
bindInstance
with factories. Can I usebindInstance
in a factory component definition at all? If not, is there any other way for me to pass the factory component function's parameter to the class constructor?Also, another documentation request: while the Quick Reference says
bindInstance
can be used for annotated types, it doesn't say how. Specifying that it'sbindInstance<fruit::Annotated<MarkerType, T>>(instance)
would be helpful.The text was updated successfully, but these errors were encountered: