Skip to content
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

Multiple fruit::Assisted arguments and registerFactory caused NoBindingFoundError of std::function #12

Closed
yuqian90 opened this issue Apr 30, 2016 · 5 comments

Comments

@yuqian90
Copy link

yuqian90 commented Apr 30, 2016

I'm trying to evaluate if i can use fruit for something production. Either I'm using fruit::Assisted wrongly or there's a bug somewhere with registerFactory or Assisted.

When the constructor of a class has multiple Assisted arguments. Compilation fails with an error like this:

fruit::impl::NoBindingFoundError<std::function<std::unique_ptr<di::TransientMultiplier>(std::basic_string<char>, int)> >’:

To reproduce the error, create a simple class like this:

class Transient
{
public:
    virtual const int getId() const = 0;

    virtual const std::string getName() const = 0;
};

And create a dependent class like this:

class TransientMultiplier
{
private:
    const Transient& transient;
    std::string suffix;
    int index;

public:
    TransientMultiplier(const Transient& transient, const std::string suffix,
        const int index)
        :transient(transient), suffix(suffix), index(index)
    {

    }

    virtual const std::string getSuffixed()
    {
        std::ostringstream ss;
        ss << transient.getName() << suffix << index;
        return ss.str();
    }
};

TransientMultiplier needs an injected Transient& in its constructor. suffix and index are value types that are supposed to be passed at runtime so they are marked as Assisted

typedef std::function<std::unique_ptr<TransientMultiplier>(std::tuple<const std::string, const int>)> TransientMultiplierFactory;

fruit::Component<fruit::Required<Transient>, TransientMultiplierFactory> getTransientMultiplierFactoryComponent()
{
    using fruit::Assisted;

    return fruit::createComponent()
        .registerFactory
            <std::unique_ptr<TransientMultiplier>(Transient* t,
                Assisted<const std::string>, Assisted<const int>)>
        (
            [](Transient* transient, const std::string suffix, const int index)
            {
                return std::unique_ptr<TransientMultiplier>(
                    new TransientMultiplier(*transient, suffix, index));
            }
        );
}

This looks okay to me. But it does not compile, with a NoBindingFoundError that looks very confusing.

In file included from /usr/include/fruit/fruit.h:25:0,
                 from ../di/Transient.cpp:2:
/usr/include/fruit/impl/injection_errors.h: In instantiation of ‘struct fruit::impl::NoBindingFoundError<std::function<std::unique_ptr<di::TransientMultiplier>(std::basic_string<char>, int)> >’:
/usr/include/fruit/impl/component.defn.h:58:3:   required from ‘fruit::Component<Types>::Component(fruit::PartialComponent<Bindings ...>) [with Bindings = {fruit::impl::RegisterFactory<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(di::Transient*, fruit::Assisted<const std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, fruit::Assisted<const int>), di::getTransientMultiplierFactoryComponent()::__lambda16>}; Params = {fruit::Required<di::Transient>, std::function<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int)>}]’
../di/Transient.cpp:126:9:   required from here
/usr/include/fruit/impl/injection_errors.h:33:3: error: static assertion failed: No explicit binding nor C::Inject definition was found for T.
   static_assert(
   ^
In file included from /usr/include/fruit/component.h:498:0,
                 from /usr/include/fruit/fruit.h:28,
                 from ../di/Transient.cpp:2:
/usr/include/fruit/impl/component.defn.h: In instantiation of ‘fruit::Component<Types>::Component(fruit::PartialComponent<Bindings ...>) [with Bindings = {fruit::impl::RegisterFactory<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(di::Transient*, fruit::Assisted<const std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, fruit::Assisted<const int>), di::getTransientMultiplierFactoryComponent()::__lambda16>}; Params = {fruit::Required<di::Transient>, std::function<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int)>}]’:
../di/Transient.cpp:126:9:   required from here
/usr/include/fruit/impl/component.defn.h:61:3: error: no type named ‘Result’ in ‘fruit::impl::meta::OpForComponent<fruit::impl::RegisterFactory<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(di::Transient*, fruit::Assisted<const std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, fruit::Assisted<const int>), di::getTransientMultiplierFactoryComponent()::__lambda16> >::ConvertTo<fruit::impl::meta::Comp<fruit::impl::meta::Vector<fruit::impl::meta::Type<di::Transient> >, fruit::impl::meta::Vector<fruit::impl::meta::Type<std::function<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int)> > >, fruit::impl::meta::Vector<fruit::impl::meta::Pair<fruit::impl::meta::Type<std::function<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int)> >, fruit::impl::meta::Vector<fruit::impl::meta::Type<di::Transient> > > >, fruit::impl::meta::Vector<>, fruit::impl::meta::EmptyList> > {aka struct fruit::impl::meta::Error<fruit::impl::NoBindingFoundErrorTag, std::function<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int)> >}’
   (void)typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
   ^
In file included from /usr/include/fruit/component.h:498:0,
                 from /usr/include/fruit/fruit.h:28,
                 from ../di/Transient.cpp:2:
/usr/include/fruit/impl/component.defn.h:64:15: error: no match for call to ‘(Op {aka fruit::impl::meta::Error<fruit::impl::NoBindingFoundErrorTag, std::function<std::unique_ptr<di::TransientMultiplier, std::default_delete<di::TransientMultiplier> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int)> >}) (fruit::impl::ComponentStorage&)’
   Op()(storage);
               ^

I can go around the problem by putting suffix and index in a std::tuple<string, int>. But that's very inconvenient. So can we not put multiple fruit::Assisted arguments in the same constructor of a class that's going to be binded via registerFactory?

I'm using libfruit Version: 2.0.0-0 on Ubuntu 14.04 installed from this deb:
deb http://download.opensuse.org/repositories/home:/poletti_marco/xUbuntu_14.04/ /

@yuqian90
Copy link
Author

Well, I played with factory and ASSISTED a bit more and I'm now more certain there's a bug somewhere.
E.g. I just slightly modified this example from the fruit test source code: tests/register_factory.cpp:

The following would not compile. I guess it's related to my issue.

#include <fruit/fruit.h>
#include <iostream>

using fruit::Component;
using fruit::Injector;
using fruit::Assisted;
using fruit::createComponent;

struct X {
  INJECT(X()) = default;
};

class Scaler {
private:
  double factor;
  int factor2;

public:
  INJECT(Scaler(ASSISTED(double) factor, ASSISTED(int) factor2, X))
    : factor(factor), factor2(factor2) {
  }

  double scale(double x) {
    return x * factor * factor2;
  }
};

using ScalerFactory = std::function<std::unique_ptr<Scaler>(double, int)>;

Component<ScalerFactory> getScalerComponent() {
  return createComponent();
}

int main() {
  Injector<ScalerFactory> injector(getScalerComponent());
  ScalerFactory scalerFactory(injector);
  std::unique_ptr<Scaler> scaler = scalerFactory(12.1, 3);
  std::cout << scaler->scale(3) << std::endl;

  return 0;
}

Compilation error:

In file included from /usr/include/fruit/fruit.h:25:0,
                 from ../di/RegisterFactoryTest.cpp:1:
/usr/include/fruit/impl/injection_errors.h: In instantiation of ‘struct fruit::impl::FunctorSignatureDoesNotMatchError<Scaler(double, int), Scaler(int, double)>’:
/usr/include/fruit/impl/component.defn.h:58:3:   required from ‘fruit::Component<Types>::Component(fruit::PartialComponent<Bindings ...>) [with Bindings = {}; Params = {std::function<std::unique_ptr<Scaler, std::default_delete<Scaler> >(double, int)>}]’
../di/RegisterFactoryTest.cpp:31:26:   required from here
/usr/include/fruit/impl/injection_errors.h:214:3: error: static assertion failed: Unexpected functor signature (it should be the same as ExpectedSignature minus any Assisted types).
   static_assert(
   ^
In file included from /usr/include/fruit/component.h:498:0,
                 from /usr/include/fruit/fruit.h:28,
                 from ../di/RegisterFactoryTest.cpp:1:
/usr/include/fruit/impl/component.defn.h: In instantiation of ‘fruit::Component<Types>::Component(fruit::PartialComponent<Bindings ...>) [with Bindings = {}; Params = {std::function<std::unique_ptr<Scaler, std::default_delete<Scaler> >(double, int)>}]’:
../di/RegisterFactoryTest.cpp:31:26:   required from here
/usr/include/fruit/impl/component.defn.h:61:3: error: no type named ‘Result’ in ‘fruit::impl::meta::OpForComponent<>::ConvertTo<fruit::impl::meta::Comp<fruit::impl::meta::Vector<>, fruit::impl::meta::Vector<fruit::impl::meta::Type<std::function<std::unique_ptr<Scaler, std::default_delete<Scaler> >(double, int)> > >, fruit::impl::meta::Vector<fruit::impl::meta::Pair<fruit::impl::meta::Type<std::function<std::unique_ptr<Scaler, std::default_delete<Scaler> >(double, int)> >, fruit::impl::meta::Vector<> > >, fruit::impl::meta::Vector<>, fruit::impl::meta::EmptyList> > {aka struct fruit::impl::meta::Error<fruit::impl::FunctorSignatureDoesNotMatchErrorTag, Scaler(double, int), Scaler(int, double)>}’
   (void)typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
   ^
In file included from /usr/include/fruit/component.h:498:0,
                 from /usr/include/fruit/fruit.h:28,
                 from ../di/RegisterFactoryTest.cpp:1:
/usr/include/fruit/impl/component.defn.h:64:15: error: no match for call to ‘(Op {aka fruit::impl::meta::Error<fruit::impl::FunctorSignatureDoesNotMatchErrorTag, Scaler(double, int), Scaler(int, double)>}) (fruit::impl::ComponentStorage&)’
   Op()(storage);
               ^

@poletti-marco
Copy link
Contributor

Thanks for the report, it was indeed a bug.
I'm releasing version 2.0.1 soon with this fix.

@poletti-marco
Copy link
Contributor

FYI, I now released 2.0.1 with this fix and the binary packages have been updated. Please update to get the fix.

@yuqian90
Copy link
Author

I gave 2.0.1 a try. It indeed fixed the problem. Thanks a lot!

poletti-marco added a commit that referenced this issue May 1, 2016
… were multiple non-assisted parameters with different types.

This is related (but not the same as) issue #12.
@poletti-marco
Copy link
Contributor

Now 2.0.2 has been released with the fix to the other related bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants