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

Adding a special constructor to one implementation of an interface #160

Closed
IrisPeter opened this issue Mar 28, 2024 · 13 comments
Closed

Adding a special constructor to one implementation of an interface #160

IrisPeter opened this issue Mar 28, 2024 · 13 comments

Comments

@IrisPeter
Copy link

When implementing a JSON repository so that it could replace the standard repository that would just load the information from disk I have now discovered a requirement to somehow modify the constructor of the JSON repository so that it can take a std::string which will be the backing store that the JSON repository object uses to load itself.

A reminder of the interface:

template <typename T, typename key_type = void, typename the_key = Key<key_type>>
class IRepository
{
public:
	using key = the_key;

	virtual ~IRepository() = default;

	virtual T GetByKey(const key& theKey = {}) = 0;

	virtual void Add(const T& entity) = 0;
	virtual void Remove(const T& entity) = 0;
	virtual void Update(const T& entity) = 0;
};

Then a section of the JSON Repository implementation

class JSONScalerRepositoryImpl : public IScalerRepository
{
public:
   INJECT(JSONScalerRepositoryImpl()) = default;

   ...
};

fruit::Component<IScalerRepository> getScalerJsonRepository()
{
	return fruit::createComponent().bind<IScalerRepository, JSONScalerRepositoryImpl>();
}

Testing.cpp

include "Testing.h"

#include "DBScalerRepository.h"
#include "JSONScalerRepository.h"


fruit::Component<ScalerFactory> getScalerTestComponent()
{
    return fruit::createComponent()
        // Note: order matters here. This replace().with() must be before the install. Otherwise Fruit will report the
        // wrong order as a run-time error.
        .replace(getScalerRepositoryDB).with(getScalerJsonRepository)
        .install(getScalerComponent);
}

main.cpp

#include <print>

#include "Repositories.h"
#include "scaler.h"

Injector<ScalerFactory> underTestInjector(getScalerTestComponent);
ScalerFactory scalerUnderTestInjector(underTestInjector);

std::unique_ptr<Scaler> scalerLoadedUnderTest = scalerUnderTestInjector(12.1);

std::println("{}", scalerLoadedUnderTest->scale(3));

So my question is there any way to modify the code to somehow get a std::string to the JSONScalerRepositoryImpl constructor?

Otherwise I think I will just have to add an extra method to the interface that would only ever be used by JSONScalerRepositoryImpl

template <typename T, typename key_type = void, typename the_key = Key<key_type>>
class IRepository
{
public:
	....
        //Same as before
        virtual void SetBackingStore(const std::string& json) = 0;
};
@poletti-marco
Copy link
Contributor

poletti-marco commented Mar 29, 2024 via email

@IrisPeter
Copy link
Author

IrisPeter commented Apr 3, 2024

I was hoping that it would be possible somehow to avoid polluting IRepository with any methods that don't look like generalised database retrieval and update methods.

SetBackingStore would be called when setting up the unit test, so it would need to be called before anyone called any of the methods of IRepository

I'm not quite sure what you were getting at with your two references, but in your Server example which uses providers I see something interesting in worker_thread_main which might be something related to what I need. When creating the injector for the RequestDispatcher you pass the Request object to it, and it seems that having the getRequest method as the 2nd parameter to the injector, and a pointer to the request argument, makes a call to .bindinstance which means that when FooHandlerImpl is created by injection it receives a reference to that original Request object.

static void worker_thread_main(const NormalizedComponent<Required<Request>, RequestDispatcher>& requestDispatcherNormalizedComponent, Request request)
{
	Injector<RequestDispatcher> injector(requestDispatcherNormalizedComponent, getRequestComponent, &request);

	RequestDispatcher* requestDispatcher(injector);
	requestDispatcher->handleRequest();
}

I also see something similar happening when setting up the NormalizedComponent.

I've noticed in these repositories that come from C# that a repository usually has a context variable in this case DbContext from EntityFramework:

public class Repository<TEntity> : IRepository <TEntity> where TEntity : class
{
      protected readonly DbContext Context;
      protected readonly DbSet<TEntity> Entities;

      public Repository(DbContext context)
      {
          Context = context;
          Entities = Context.Set<TEntity>();
      }

      public void Add(TEntity entity)
      {
          Entities.Add(entity);
      }
}

Upon seeing this I asked ChatGPT about other context objects that other frameworks might use in their repositories and it referred to objects from ADO.NET and NHibernate. As in the above example they receive one of these contexts via DI in their constructors.

The different context objects are much more fully featured than anything I might need, I think that I would only need a std::string to represent the JSON object that would allow the repository to return an object.

For my requirements it is only the JSON version of the repository that needs a context object, as all the DB implementations would just feature one of our special DAO objects and they are able to retrieve the DB connections/strings without being explicitly stated.

I imagine that both repositories will need to have context objects passed to their constructors, the string would just be empty for the DB repositories.

I would imagine I would probably do the same thing as you do in the example where the string lives as a member of some struct, say JsonContext or maybe I would just call it DbContext, I'm not sure what would make sense to readers of any finished code I might come up with.

I find certain parts of the Fruit library hard to get my head around, so even though I have stepped through the Server example I still require some guidance on how I can get this to work.

@poletti-marco
Copy link
Contributor

Could you pass the backing store as a param to getScalerTestComponent and use bindInstance() there?
Something like:

class JSONScalerRepositoryImpl : public IScalerRepository
{
private:
  std::string& backingStore;
public:
   INJECT(JSONScalerRepositoryImpl(std::string& backingStore)) : backingStore(backingStore) {};
   ...
};
fruit::Component<ScalerFactory> getScalerTestComponent(std::string& backingStore)
{
    return fruit::createComponent()
        .bindInstance(backingStore)
        // Note: order matters here. This replace().with() must be before the install. Otherwise Fruit will report the
        // wrong order as a run-time error.
        .replace(getScalerRepositoryDB).with(getScalerJsonRepository)
        .install(getScalerComponent);
}
std::string backingStore = ...;
Injector<ScalerFactory> underTestInjector(getScalerTestComponent, backingStore);

@IrisPeter
Copy link
Author

IrisPeter commented Apr 8, 2024

The modifications to Testing.cpp are compiling OK.

However when I try and compile JSONScalerRepository.cpp I receive the following compilation errors:

1>------ Build started: Project: RepositoryExample, Configuration: Debug x64 ------
1>Scanning sources for module dependencies...
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\injection_errors.h(33,33): error C2338: static_assert failed: 'No explicit binding nor C::Inject definition was found for T.'
1>  static_assert(AlwaysFalse<T>::value, "No explicit binding nor C::Inject definition was found for T.");
1>                                ^ (compiling source file 'JSONScalerRepository.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\injection_errors.h(33,33):
1>    the template instantiation context (the oldest one first) is
1>      static_assert(AlwaysFalse<T>::value, "No explicit binding nor C::Inject definition was found for T.");
1>                                    ^
1>        C:\Dev\VisualStudio Projects\Visual Studio 2022\Projects\C++\Examples\Fruit\RepositoryAttempts\WithBackingStore-RepositoryExample\JSONScalerRepository.cpp(41,2):
1>        see reference to function template instantiation 'fruit::Component<IScalerRepository>::Component<fruit::impl::Bind<IScalerRepository,JSONScalerRepositoryImpl>>(fruit::PartialComponent<fruit::impl::Bind<IScalerRepository,JSONScalerRepositoryImpl>> &&) noexcept' being compiled
1>        	return fruit::createComponent().bind<IScalerRepository, JSONScalerRepositoryImpl>();
1>        	^
1>            C:\Dev\VisualStudio Projects\Visual Studio 2022\Projects\C++\Examples\Fruit\RepositoryAttempts\WithBackingStore-RepositoryExample\JSONScalerRepository.cpp(41,2):
1>            see the first reference to 'fruit::Component<IScalerRepository>::Component' in 'getScalerRepositoryJSONComponent'
1>            	return fruit::createComponent().bind<IScalerRepository, JSONScalerRepositoryImpl>();
1>            	^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(56,59):
1>        see reference to class template instantiation 'fruit::impl::NoBindingFoundError<Context>' being compiled
1>          (void)typename fruit::impl::meta::CheckIfError<Op>::type();
1>                                                                  ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039: 'Result': is not a member of 'fruit::impl::meta::Error<ErrorTag,Context>'
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:         with
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:         [
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:             ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:         ]
1>      fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                                                                ^ (compiling source file 'JSONScalerRepository.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\basics.h(63,8):
1>    see declaration of 'fruit::impl::meta::Error<ErrorTag,Context>'
1>        with
1>        [
1>            ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>        ]
1>    struct Error {};
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>      fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                                                                ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,47): error C2825: 'Comp': must be a class or namespace when followed by '::'
1>    using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                              ^ (compiling source file 'JSONScalerRepository.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,47):
1>    the template instantiation context (the oldest one first) is
1>        using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                                  ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,26):
1>        see reference to alias template instantiation 'fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(int)>' being compiled
1>              fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                 ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(253,41):
1>        see reference to class template instantiation 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>' being compiled
1>        using Eval = typename DoEval<MetaExpr>::type;
1>                                                ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76):
1>        see reference to class template instantiation 'fruit::impl::meta::CheckNoLoopInDeps::apply<MetaExpr>' being compiled
1>        with
1>        [
1>            MetaExpr=int
1>        ]
1>              MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                   ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,53): error C2510: 'Comp': left of '::' must be a class/struct/union
1>    using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                                    ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,53): error C2061: syntax error: identifier 'Deps'
1>    using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                                    ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(955,21): error C2226: syntax error: unexpected type 'fruit::impl::meta::IsNone'
1>    using type = If(IsNone(Loop), Bool<true>, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop));
1>                    ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(955,74): error C2059: syntax error: '('
1>    using type = If(IsNone(Loop), Bool<true>, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop));
1>                                                                         ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039: 'type': is not a member of 'fruit::impl::meta::CheckNoLoopInDeps::apply<MetaExpr>'
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:         with
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:         [
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:             MetaExpr=int
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:         ]
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                           ^ (compiling source file 'JSONScalerRepository.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(953,10):
1>    see declaration of 'fruit::impl::meta::CheckNoLoopInDeps::apply<MetaExpr>'
1>        with
1>        [
1>            MetaExpr=int
1>        ]
1>      struct apply {
1>             ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2146: syntax error: missing '>' before identifier 'type'
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                           ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,83): error C2039: 'type': is not a member of '`global namespace''
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                  ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,83): error C2146: syntax error: missing ';' before identifier 'type'
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                  ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,83): error C2602: 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>::type' is not a member of a base class of 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>'
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                  ^ (compiling source file 'JSONScalerRepository.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(100,9):
1>    see declaration of 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>::type'
1>      using type = typename DoEval<typename std::conditional<
1>            ^
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(93,8):
1>    see declaration of 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>'
1>    struct DoEval<MetaFun(MetaExprs...)> {
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,92): error C2955: 'fruit::impl::meta::CheckIfError': use of class template requires template argument list
1>      fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                                                                           ^ (compiling source file 'JSONScalerRepository.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\errors.h(28,8):
1>    see declaration of 'fruit::impl::meta::CheckIfError'
1>    struct CheckIfError {
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039: 'numEntries': is not a member of 'fruit::impl::meta::Error<ErrorTag,Context>'
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:         with
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:         [
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:             ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:         ]
1>  std::size_t num_entries = partial_component.storage.numBindings() + Op().numEntries();
1>                                                                           ^ (compiling source file 'JSONScalerRepository.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\basics.h(63,8):
1>    see declaration of 'fruit::impl::meta::Error<ErrorTag,Context>'
1>        with
1>        [
1>            ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>        ]
1>    struct Error {};
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(66,5): error C2064: term does not evaluate to a function taking 1 arguments
1>  Op()(entries);
1>    ^ (compiling source file 'JSONScalerRepository.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(51,100): error C1907: unable to recover from previous error(s); stopping compilation
1>inline Component<Params...>::Component(PartialComponent<Bindings...>&& partial_component) noexcept : storage() {
1>                                                                                                   ^ (compiling source file 'JSONScalerRepository.cpp')
1>JSONScalerRepository.cpp
1>Done building project "RepositoryExample.vcxproj" -- FAILED.

main.cpp I am receiving the following compilation errors:

1>------ Build started: Project: RepositoryExample, Configuration: Debug x64 ------
1>Scanning sources for module dependencies...
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component_install_arg_checks.defn.h(34,13): error C2440: 'initializing': cannot convert from 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' to 'std::string &'
1>        T x2(std::move(value));
1>            ^ (compiling source file 'RepositoryExample.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component_install_arg_checks.defn.h(34,13):
1>    A non-const reference may only be bound to an lvalue
1>            T x2(std::move(value));
1>                ^
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component_install_arg_checks.defn.h(34,13):
1>    the template instantiation context (the oldest one first) is
1>            T x2(std::move(value));
1>                ^
1>        C:\Dev\VisualStudio Projects\Visual Studio 2022\Projects\C++\Examples\Fruit\RepositoryAttempts\RepositoryExample\RepositoryExample.cpp(159,53):
1>        see reference to function template instantiation 'fruit::Injector<ScalerFactory>::Injector<std::string&,std::string&>(fruit::Component<ScalerFactory> (__cdecl *)(std::string &),std::string &)' being compiled
1>            fruit::Injector<ScalerFactory> underTestInjector(getScalerTestComponent, backingStore);
1>                                                            ^
1>            C:\Dev\VisualStudio Projects\Visual Studio 2022\Projects\C++\Examples\Fruit\RepositoryAttempts\RepositoryExample\RepositoryExample.cpp(159,53):
1>            see the first reference to 'fruit::Injector<ScalerFactory>::Injector' in 'scaler_main'
1>                fruit::Injector<ScalerFactory> underTestInjector(getScalerTestComponent, backingStore);
1>                                                                ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\injector.defn.h(30,56):
1>        see reference to function template instantiation 'fruit::PartialComponent<fruit::impl::InstallComponent<fruit::Component<ScalerFactory> (std::string &)>> fruit::PartialComponent<>::install<ScalerFactory,std::string&,std::string&>(fruit::Component<ScalerFactory> (__cdecl *)(std::string &),std::string &)' being compiled
1>          Component<P...> component = fruit::createComponent().install(getComponent, std::forward<Args>(args)...);
1>                                                               ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(250,38):
1>        see reference to function template instantiation 'int fruit::impl::checkAcceptableComponentInstallArg<std::string&>(void)' being compiled
1>          (void)IntCollector{0, fruit::impl::checkAcceptableComponentInstallArg<FormalArgs>()...};
1>                                             ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component_install_arg_checks.defn.h(38,37): error C2064: term does not evaluate to a function taking 1 arguments
1>        std::size_t h = std::hash<T>()(constRef);
1>                                    ^ (compiling source file 'RepositoryExample.cpp')
1>RepositoryExample.cpp
1>Done building project "RepositoryExample.vcxproj" -- FAILED.

JSONScalerRepository.cpp

#include "JSONScalerRepository.h"

class JSONScalerRepositoryImpl : public IScalerRepository
{
private:
  std::string& backingStore;
public:
   INJECT(JSONScalerRepositoryImpl(std::string& backingStore)) : backingStore(backingStore) {};

	//INJECT(JSONScalerRepositoryImpl()) = default;

	scaler GetByKey(const key& theKey = {}) override
	{
		std::println("GetByKey called from the JSON Scaler Repository");
		scaler scale;
		scale.factor = 5.5;

		return scale;
	}

	void Add(const scaler& item) override
	{
	}

	void Update(const scaler& item) override
	{
	}

	void Remove(const scaler& key) override
	{
	}

};

fruit::Component<IScalerRepository> getScalerRepositoryJSONComponent()
{
	return fruit::createComponent().bind<IScalerRepository, JSONScalerRepositoryImpl>();
}

@IrisPeter
Copy link
Author

I also tried turning the std::string into a struct holding a string like the Request object in the Server example.

That gave slightly different errors, in the main.cpps the main error was inside this bit of the fruit code

template <typename T>
FRUIT_ALWAYS_INLINE inline int checkAcceptableComponentInstallArg() {
    // This lambda checks that the required operations on T exist.
    // Note that the lambda is never actually executed.
    auto checkRequirements = [](const T& constRef, T value) {
        T x1(constRef);
        T x2(std::move(value));
        x1 = constRef;
        x2 = std::move(value);
        bool b = (constRef == constRef);
        std::size_t h = std::hash<T>()(constRef);
        (void)x1;
        (void)x2;
        (void)b;
        (void)h;
    };
    (void)checkRequirements;
    return 0;
}

Its the std::move line when using std::string, and the x1 = constRef; line if the Context struct.

Inside JSONScalerRepository.cpp its the following code that fails:

1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(56,59):
1>        see reference to class template instantiation 'fruit::impl::NoBindingFoundError<Context>' being compiled
1>          (void)typename fruit::impl::meta::CheckIfError<Op>::type();
1>                                                                  ^

The only thing that changes is that Context in the code we started with was 1> see declaration of 'fruit::impl::meta::Error<ErrorTag,std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'

I'm not sure what is going on, as it doesn't seem that different to the Server example.

@IrisPeter
Copy link
Author

I wondered whether the return type of both getScalerRepositoryDB and getScalerJsonRepository needed to be changed to:

fruit::Component<fruit::Required<std::string>, IScalerRepository> getScalerJsonRepository();
fruit::Component<fruit::Required<std::string>, IScalerRepository> getScalerRepositoryDBComponent();

and whilst that seemed to fix the compiling of JSONScalerRepository.cpp, it seemed to break compiling Scaler.cpp.

Also whilst Testing.cpp compiled OK with getScalerTestComponent having std::string& backingStore as its parameter, the creation of the injector in main.cpp did not like it, but when I changed the parameter to std::string backingStore, then it was OK with the change, but I was still left with Scaler.cpp not compiling with the following error:

1>Scanning sources for module dependencies...
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\injection_errors.h(33,33): error C2338: static_assert failed: 'No explicit binding nor C::Inject definition was found for T.'
1>  static_assert(AlwaysFalse<T>::value, "No explicit binding nor C::Inject definition was found for T.");
1>                                ^ (compiling source file 'Scaler.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\injection_errors.h(33,33):
1>    the template instantiation context (the oldest one first) is
1>      static_assert(AlwaysFalse<T>::value, "No explicit binding nor C::Inject definition was found for T.");
1>                                    ^
1>        C:\Dev\VisualStudio Projects\Visual Studio 2022\Projects\C++\Examples\Fruit\RepositoryAttempts\RepositoryExample\Scaler.cpp(46,5):
1>        see reference to function template instantiation 'fruit::Component<ScalerFactory>::Component<fruit::impl::InstallComponent<fruit::Component<fruit::Required<std::string>,IScalerRepository> (void)>,fruit::impl::Bind<Scaler,ScalerImpl>>(fruit::PartialComponent<fruit::impl::InstallComponent<fruit::Component<fruit::Required<std::string>,IScalerRepository> (void)>,fruit::impl::Bind<Scaler,ScalerImpl>> &&) noexcept' being compiled
1>            return createComponent().bind<Scaler, ScalerImpl>().install(getScalerRepositoryDBComponent);
1>            ^
1>            C:\Dev\VisualStudio Projects\Visual Studio 2022\Projects\C++\Examples\Fruit\RepositoryAttempts\RepositoryExample\Scaler.cpp(46,5):
1>            see the first reference to 'fruit::Component<ScalerFactory>::Component' in 'getScalerComponent'
1>                return createComponent().bind<Scaler, ScalerImpl>().install(getScalerRepositoryDBComponent);
1>                ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(56,59):
1>        see reference to class template instantiation 'fruit::impl::NoBindingFoundError<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>' being compiled
1>          (void)typename fruit::impl::meta::CheckIfError<Op>::type();
1>                                                                  ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039: 'Result': is not a member of 'fruit::impl::meta::Error<ErrorTag,std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:         with
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:         [
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:             ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C2039:         ]
1>      fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                                                                ^ (compiling source file 'Scaler.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\basics.h(63,8):
1>    see declaration of 'fruit::impl::meta::Error<ErrorTag,std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'
1>        with
1>        [
1>            ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>        ]
1>    struct Error {};
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,81): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>      fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                                                                ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,47): error C2825: 'Comp': must be a class or namespace when followed by '::'
1>    using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                              ^ (compiling source file 'Scaler.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,47):
1>    the template instantiation context (the oldest one first) is
1>        using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                                  ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,26):
1>        see reference to alias template instantiation 'fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(int)>' being compiled
1>              fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                 ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(253,41):
1>        see reference to class template instantiation 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>' being compiled
1>        using Eval = typename DoEval<MetaExpr>::type;
1>                                                ^
1>        C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76):
1>        see reference to class template instantiation 'fruit::impl::meta::CheckNoLoopInDeps::apply<MetaExpr>' being compiled
1>        with
1>        [
1>            MetaExpr=int
1>        ]
1>              MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                   ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,53): error C2510: 'Comp': left of '::' must be a class/struct/union
1>    using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                                    ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(954,53): error C2061: syntax error: identifier 'Deps'
1>    using Loop = ProofForestFindLoop(typename Comp::Deps);
1>                                                    ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(955,21): error C2226: syntax error: unexpected type 'fruit::impl::meta::IsNone'
1>    using type = If(IsNone(Loop), Bool<true>, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop));
1>                    ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(955,74): error C2059: syntax error: '('
1>    using type = If(IsNone(Loop), Bool<true>, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop));
1>                                                                         ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039: 'type': is not a member of 'fruit::impl::meta::CheckNoLoopInDeps::apply<MetaExpr>'
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:         with
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:         [
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:             MetaExpr=int
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2039:         ]
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                           ^ (compiling source file 'Scaler.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\component.h(953,10):
1>    see declaration of 'fruit::impl::meta::CheckNoLoopInDeps::apply<MetaExpr>'
1>        with
1>        [
1>            MetaExpr=int
1>        ]
1>      struct apply {
1>             ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,76): error C2146: syntax error: missing '>' before identifier 'type'
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                           ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,83): error C2039: 'type': is not a member of '`global namespace''
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                  ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,83): error C2146: syntax error: missing ';' before identifier 'type'
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                  ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(102,83): error C2602: 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>::type' is not a member of a base class of 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>'
1>      MetaFun>::type::template apply<typename DoEval<MetaExprs>::type...>::type>::type;
1>                                                                                  ^ (compiling source file 'Scaler.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(100,9):
1>    see declaration of 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>::type'
1>      using type = typename DoEval<typename std::conditional<
1>            ^
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\eval.h(93,8):
1>    see declaration of 'fruit::impl::meta::DoEval<fruit::impl::meta::CheckNoLoopInDeps (int)>'
1>    struct DoEval<MetaFun(MetaExprs...)> {
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(60,92): error C2955: 'fruit::impl::meta::CheckIfError': use of class template requires template argument list
1>      fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
1>                                                                                           ^ (compiling source file 'Scaler.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\errors.h(28,8):
1>    see declaration of 'fruit::impl::meta::CheckIfError'
1>    struct CheckIfError {
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039: 'numEntries': is not a member of 'fruit::impl::meta::Error<ErrorTag,std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:         with
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:         [
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:             ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(63,76): error C2039:         ]
1>  std::size_t num_entries = partial_component.storage.numBindings() + Op().numEntries();
1>                                                                           ^ (compiling source file 'Scaler.cpp')
1>    C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\meta\basics.h(63,8):
1>    see declaration of 'fruit::impl::meta::Error<ErrorTag,std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'
1>        with
1>        [
1>            ErrorTag=fruit::impl::NoBindingFoundErrorTag
1>        ]
1>    struct Error {};
1>           ^
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(66,5): error C2064: term does not evaluate to a function taking 1 arguments
1>  Op()(entries);
1>    ^ (compiling source file 'Scaler.cpp')
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(51,100): error C1907: unable to recover from previous error(s); stopping compilation
1>inline Component<Params...>::Component(PartialComponent<Bindings...>&& partial_component) noexcept : storage() {
1>                                                                                                   ^ (compiling source file 'Scaler.cpp')
1>Scaler.cpp
1>Done building project "RepositoryExample.vcxproj" -- FAILED.

@poletti-marco
Copy link
Contributor

Oh sorry I forgot the component types had to be the same since you're using replace(...).with(...).
Try this:

fruit::Component<IScalerRepository> getScalerJsonRepository(std::string& jsonString) {
	return fruit::createComponent()
            .bind<IScalerRepository, JSONScalerRepositoryImpl>()
            .bindInstance(jsonString);
}
// As before
fruit::Component<IScalerRepository> getScalerRepositoryDBComponent() {...}
fruit::Component<ScalerFactory> getScalerTestComponent(std::string& jsonString) {
    return fruit::createComponent()
        // Note: order matters here. This replace().with() must be before the install. Otherwise Fruit will report the
        // wrong order as a run-time error.
        .replace(getScalerRepositoryDB).with(getScalerJsonRepository, jsonString)
        .install(getScalerComponent);
}

@IrisPeter
Copy link
Author

IrisPeter commented Apr 10, 2024

Thanks @poletti-marco ,

I'm further along, I can manage to get the std::string version to compile.

The compiler won't accept your specific changes, I had to remove the & from fruit::Component<ScalerFactory> getScalerTestComponent(std::string& jsonString) and from getScalerJsonRepository otherwise it fails inside checkAcceptableComponentInstallArg again.

In the struct Context version (containing a std::string) I had to add the following inside the struct:

auto operator<=>(const Context&) const = default;

and also outside the struct:

namespace std
{
    template <>
    struct hash<Context>
    {
        size_t operator()(const Context& ctx) const { return std::hash<std::string>()(ctx.BackingStore); }
    };
}

The problem I'm having is that the repository crashes the moment you access the backingStore, at first I didn't get around to referencing the backingStore and so nothing crashed, however the moment I tried to access it we get a crash.

If you modify JSONScalerRepositoryImpl as follows:

class JSONScalerRepositoryImpl : public IScalerRepository
{
private:
  std::string backingStore;
public:
   INJECT(JSONScalerRepositoryImpl(std::string info)) : backingStore(info) {};
       ...

The crash will move to the injection site (an STL member throws when trying to fulfil the constructor.

I then wondered whether the Component functions had to take pointers because the working Server example the function getRequestComponent takes a Request* rather than a 'Request' and then it dereferences the pointer:

static Component<Request> getRequestComponent(Request* request)
{
	return createComponent().bindInstance(*request);
}

When the request is injected at that point in the Server example it does bind as a const Request*.

I tried that modification both in my std::string version and the Context version.

In Context version
Testing.cpp compiles OK
main.cpp errors with Error C2440 'initializing': cannot convert from 'initializer list' to 'std::tuple<Context *>

In std::string version
Testing.cpp errors with Error C2440 'initializing': cannot convert from 'initializer list' to 'std::tuple<std::string>
main.cpp compiles OK

@IrisPeter
Copy link
Author

Just noticed

In the std::string version I had left the following in Testing.h/cpp:

fruit::Component<ScalerFactory> getScalerTestComponent(std::string)

When I changed that to std::string* the error disappears in Testing.cpp.

@IrisPeter
Copy link
Author

So now I have a working version with std::string as long as all the parameters are std::string*

The Context version just has the error I showed earlier:

image

1>------ Build started: Project: RepositoryExample, Configuration: Debug x64 ------
1>Scanning sources for module dependencies...
1>C:\Dev\Libraries\fruit-3.7.1\out\install\x64-Debug\Include\fruit\impl\component.defn.h(255,39): error C2440: 'initializing': cannot convert from 'initializer list' to 'std::tuple<Context *>'
1>RepositoryExample.cpp
1>Done building project "RepositoryExample.vcxproj" -- FAILED.

For some reason the fuller error is only showing in the problem details view.

Its confusing why the Context* version doesn't work as the Server example in the Fruit examples is very similar

@poletti-marco
Copy link
Contributor

I'm guessing there's a mismatch somewhere.
std::string doesn't have any special handling so if you just replace std::string with Context everywhere that interacts with Fruit then it should still work.
Can you share the parts of the code that reference Context?

@IrisPeter
Copy link
Author

IrisPeter commented Apr 11, 2024

So now I have a working version with std::string as long as all the parameters are std::string*

The Context version just has the error I showed earlier:

image

For some reason the fuller error is only showing in the problem details view.

Its confusing why the Context* version doesn't work as the Server example in the Fruit examples is very similar

Scratch the above I had forgotten to update the variable passed to the Injector constructor so that it was a Context object rather than a std::string

It's all now working, thanks @poletti-marco

So in summary for anyone who looks at this issue wanting to do something similar, the steps where:

fruit::Component<IScalerRepository> getScalerJsonRepository(std::string* pJsonString) 
{
	return fruit::createComponent()
            .bind<IScalerRepository, JSONScalerRepositoryImpl>()
            .bindInstance(*pJsonString);
}
// As before
fruit::Component<IScalerRepository> getScalerRepositoryDBComponent() {...}
fruit::Component<ScalerFactory> getScalerTestComponent(std::string* pJsonString) 
{
    return fruit::createComponent()
        // Note: order matters here. This replace().with() must be before the install. Otherwise Fruit will report the
        // wrong order as a run-time error.
        .replace(getScalerRepositoryDB).with(getScalerJsonRepository, pJsonString)
        .install(getScalerComponent);
}

and finally

class JSONScalerRepositoryImpl : public IScalerRepository
{
private:
   std::string m_context;
public:
   INJECT(JSONScalerRepositoryImpl(const std::string& json)) : m_context(json) {}
   ...
};

So both the spaceship operator inside Context and the hash implementation were irrelevant, they were only required when the code had a either a plain object or a reference to an object, so I have removed them.

@IrisPeter
Copy link
Author

Closing Issue due to working instructions in thread

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