(p)->eval()" at run-time from th" /> (p)->eval()" at run-time from th" /> (p)->eval()" at run-time from th"/>

removing overhead of dynamic_cast codegen with minimum boilerplate

59 views Asked by At

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.

1

There are 1 answers

0
AudioBubble On

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:


// add struct
template<typename T=void>
struct Crtp{
    virtual std::string my_typename()const{ return typename_of<T>(); }
};

struct B:Crtp<B>{
    virtual void eval()=0;
};

template<typename T>
struct C: A,B,Crtp<C<T>>{
    void eval(){} 
};

The string Crtp<structname> should essentially make the boilerplate virtual 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 both B and Crtp<C<T>>, the routine C<T>::my_typename is 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).