Consider the following code:
typedef void(__cdecl *logging_fnc)(int, const char*, ...);
template<logging_fnc LogFnc>
class Logger {
public:
void foo()
{
LogFnc(1, "Foo");
}
};
template<class Target, Target& instance>
void MakeLogAtTarget(int lvl, const char* msg...)
{
instance.log("specific foo", lvl, msg);
}
The Logger
somewhere simply calls the non-type template LogFnc
from some method.
Now I also have a singleton class MyTarget
.
class MyTarget {
public:
void log(const char* targetSpecific, int lvl, const char* msg)
{
printf("%s %s", targetSpecific, msg);
}
static MyTarget& GetInstance()
{
static MyTarget myInstance;
return myInstance;
}
private:
MyTarget() = default;
};
Why exactly the following doesn't work?
using TargetLogger = Logger<MakeLogAtTarget<MyTarget, MyTarget::GetInstance()>>;
I keep getting: cannot convert from 'void (__cdecl *)(int,const char *,...)' to 'logging_fnc'
Is it simply because the compiler cannot get the proper address of the specialization of MakeLogAtTarget
it generates?
Generally speaking: is it possible to feed the non-template argument a pointer to a function which in turn invokes a method from some class instance?
UPDATE : Also tried
static const MyTarget targetInstance = MyTarget::GetInstance();
using TargetLogger = Logger<MakeLogAtTarget<MyTarget, targetInstance)>>;
Still the same error.
UPDATE
static const MyTarget targetInstance = MyTarget::GetInstance();
static const logging_fnc targetFnc = MakeLogAtTarget<MyTarget, targetInstance>;
using TargetLogger = Logger<targetFnc>;
outputs the same error at the line where targetFnc
is declared, moreover an additional error appears at the last line: 'targetFnc': an expression involving objects with internal linkage cannot be used as a non-type argument
The issue is you're failing to meet the requirements for the template non-type argument of type Target&
. From [temp.arg.nontype]:
A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter .
Where a converted constant expression prohibits, from [expr.const]:
A conditional-expression
e
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine (1.9), would evaluate one of the following expressions: [...] an invocation of a function other than aconstexpr
constructor for a literal class, aconstexpr
function, or an implicit invocation of a trivial destructor (12.4)
You'll need some specific MyTarget
object to refer to (or, in a post- N4198 world, a constexpr
function that returns such a thing or something similar). Like:
struct MyTarget { ... };
static MyTarget x;
using TargetLogger = Logger<MakeLogAtTarget<MyTarget,x>>;
The problem can probably be reduced to a simple
template <typename T, T& t> void bar() {}
struct Z
{
static constexpr Z& get();
};
static Z z;
constexpr Z& Z::get() { return z; }
int main()
{
bar<Z, Z::get()>();
}
In C++11 and C++14 modes GCC responds with
main.cpp: In function 'int main()':
main.cpp:14:22: error: no matching function for call to 'bar()'
bar<Z, Z::get()>();
^
main.cpp:1:34: note: candidate: template<class T, T& t> void bar()
template <typename T, T& t> void bar() {}
^~~
main.cpp:1:34: note: template argument deduction/substitution failed:
main.cpp:14:22: error: 'Z::get()' is not a valid template argument for type 'Z&' because it is not an object with linkage
bar<Z, Z::get()>();
^
It does not like your attempt to use a function return as an argument for a reference-typed template parameter.
Once the code becomes more complicated (like yours) the descriptive error messages disappear, replaced with the message you quoted or a very similar one.
Yet, it compiles fine in -std=c++17
mode.
It's not clear what you are trying to achieve. It seems that you have a singleton instance that you want to feed to a logger? I'm missing the bigger picture.
template<class Target, Target& instance>
Here it seems you want to use an instance of an object as a template parameter. However, afaik templates are a compile-time construct, while the instance is generated at run-time. This construction would be impossible in that case. But I'm no expert.
edit: hmm, a -1 feedback modifier again. The guys/gal doing that should comment why.
edit 2: So now I understand your problem better. But then again the question: is it required to have MakeLogAtTarget
fully template-able?
What you can do is use function pointers as template arguments. Eg:
template<class Target, Target& (*Getter)()>
void MakeLogAtTarget(int lvl, const char* msg...)
{
Getter().log("specific foo", lvl, msg);
}
Thus you can call it using:
using TargetLogger = Logger < MakeLogAtTarget < MyTarget, MyTarget::GetInstance >> ;
Is that what you are looking for?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.