How can I obtain all necessary information in order to print an equivalent faster string of code for "dynamic_cast<B&>(p)->eval()" at run-time from the code below? (please find code-generator background/context below code example)
#include<iostream>
#include<string>
#include"lib_typename.hpp" // template<typename T> typename_of<T>() returns string "T"; cf., e.g., in Boost
struct X;
struct A{
virtual std::string my_typename()const{ return typename_of<A>(); }
virtual ~A(){};
};
struct B{
virtual std::string my_typename()const{ return typename_of<B>(); }
virtual void eval()=0;
};
template<typename T>
struct C: A,B{
virtual std::string my_typename()const{ return typename_of<C<T>>(); }
void eval(){}
};
int main(){
C<X> c;
A& p = c;
dynamic_cast< B& >(p).eval(); // <-- I can generate that, but it is slow.
static_cast< C<X>& >(p).eval(); // <-- This is fast, but where do I get the string "C<X>" from?
// possible ways to find the string:
std::cout << p.my_typename() << "\n"; // necessitates tons of boilerplate.
std::cout << typeid(C<X>).name() << "\n"; // returns gibberish.
}
For explanation: The line "dynamic_cast<B&>(p).eval()" is generated by a code-generator that I built. At generation time, I have the information at hand that A& p refers to a non-abstract derived of B.
My current favorite would be the boilerplate option, so that my code-generator eventually can produce the faster code "static_cast<C&>(p).eval()". But is there anything possible with less boilerplate?
Background: My application is a template-based code-generator. Some may call this in itself an xy problem but my x works (apart from the above, which I could live with) satisfactory. The question is vague because I am equally happy with an alternative solution that does not identify the string "C<X>".
Final remark: I added the template argument X as minimum counter example to show that finding the first common inheritor of A and B does not suffice for a solution.
Based on @CraigEstey's remarkable "blind guess", the following solution follows the boilerplate approach while mitigating some of the boilerplate by replacing it by virtue of CRTP inheritance:
The string
Crtp<structname>should essentially make the boilerplatevirtual std::string my_typename()const{ return typename_of<structname>(); }unnecessary, but I have not tested it yet. I am not sure yet. Everyones' solutions are welcome.It does not work in the above form
By
C<T>inheriting bothBandCrtp<C<T>>, the routineC<T>::my_typenameis ambiguous with a compile error in accordance to this fact. I see here [https://stackoverflow.com/questions/73536861/how-to-handle-multiple-inheritance-of-same-methods-or-diamond-problem-with-crtp] that ambiguous definitions in CRTP can be erased. However, I do not see whether this applies as well to functions (and/or how this would be programmed in a readable way) and how the hierarchy would be enforced (e.g., in that C::my_typename overwrites B::my_typename).