I have looked at several similar questions on SO. Maybe I am not grokking the solutions there. In those questions when the return type is auto
or templated then separating declaration and definition in two different units causes a failure in compilation. This can be solved by explicitly declaring a concrete signature for the function definition. In my case I am not sure how to do that.
My scenario is as below:
// api.h
template <typename TImpl>
concept IsAProcessor = requires(TImpl impl)
{
impl.init();
impl.process();
impl.deinit();
};
enum UseCase {
USECASE1,
USECASE2
};
template <IsAProcessor TImpl>
void Process(TImpl& impl)
{
impl.process();
}
class Engine
{
public:
IsAProcessor auto getInstance(UseCase a);
};
// End - api.h
// api.cpp
#include "api.h"
#include "third_party.h"
IsAProcessor auto Engine::getInstance(UseCase a) {
switch (UseCase) {
case USECASE1:
return UseCase1Impl(); // Defined in third_party.h and satisfies concept requirement.
case USECASE2:
return UseCase2Impl();
}
}
// End - api.cpp
// third_party.h
class UseCase1Impl {
public:
void init(void);
void process(void);
void deinit(void);
}
// End - third_party.h
// third_party.cpp
#include "third_party.h"
void UseCase1Impl::init(void) {...};
// and so forth
// End - third_party.cpp
// User code
#include "api.h"
{
auto en = Engine();
auto usecase = en.getInstance(UseCase::USECASE1);
//^^^ cannot be used before it is defined here
Process(usecase);
}
As I mentioned in the question, it is not desirable to expose UseCase1Impl and UseCase2Impl. How do I get past the error: function 'getInstance' with deduced return type cannot be used before it is defined
CodePudding user response:
The return type of a function is a static property, it can't change based on runtime data.
If you can, lift UseCase
to a template parameter, and use if constexpr
to have exactly one active return
for each instantiation.
template<UseCase a>
auto Engine::getInstance() {
if constexpr (a == USECASE1)
return UseCase1Impl(); // Defined in third_party.h and satisfies concept requirement.
if constexpr (a == USECASE2)
return UseCase2Impl();
}
If you can't do that, you will have to find a common type to return.
struct IProcessor
{
virtual ~IProcessor() = default;
virtual void init() = 0;
virtual void process() = 0;
virtual void deinit() = 0;
};
template <IsAProcessor T>
class ProcessorFacade : public IProcessor
{
T impl;
public:
template <typename... Args>
ProcessorFacade(Args&&... args) : impl(std::forward<Args>(args)...) {}
void init() final { impl.init(); }
void process() final { impl.process(); }
void deinit() final { impl.deinit(); }
};
std::unique_ptr<IProcessor> Engine::getInstance(UseCase a) {
switch (UseCase) {
case USECASE1:
return std::make_unique<ProcessorFacade<UseCase1Impl>>();
case USECASE2:
return std::make_unique<ProcessorFacade<UseCase2Impl>>();
}
}