It’s not going to be possible to build a functional model stub before you have a real model. The trick is going to be figuring out how to break everything into two translation units:
everything but the model; includes model_base.hpp and all references to models should be to references to model_base.hpp.
the mode; this needs to be something that implements model_base.hpp, so just what comes out of the C++ compiler (which now excludes the factory function).
What I don’t know how to do is dynamically link what you compile in (2) together with (1). What I did for CmdStan was have CmdStan invoke a new_model function in translation unit 1 using only the new_model() header. Then translation unit 2 included the definition of new_model().
How do I tell the compiler that the model_base class is already compiled when it goes to compile the thing that inherits from model_base_crtp? Do I just link to the RStan shared object? Also, why is there no gradient-related virtual methods in model_base. Am I still supposed to be calling log_prob_grad, which is still templated on the model?
I do not understand the code that I wrote, but I don’t understand what you are saying either. The C++ class in RStan that runs everything can’t be defined in terms of model_base_crtp<> because we don’t know what to put for the template parameter until the C++ code is generated for the user’s model. So, AFAICT, it has to be defined as (a pointer to) model_base. But then, I can’t call methods that are defined in the thing that inherits from model_base_crtp<>; I have to call the standalone function that is templated on the model: https://github.com/stan-dev/rstan/blob/for_2.20/rstan/rstan/src/stan_fit.cpp#L1090
That makes more sense but the generated C++ #include <stan/model/model_header.hpp> which #include <stan/model/model_base.hpp so isn’t it recompiling the model_base class from the header file before it even gets to the linker?
I forgot a few details about model_base so let me detail a bit (after having looked at the code):
mode_base defines all the functions of the model which involve templates as virtual functions. Thus, all functions like log_prob are declared using overloading, but none of them is actually implemented as all are merely virtual.
model_base_crtp actually implements all the different overloads of these functions like log_probusing the templated model.
With this design everything you ever want to do is available in the model_base. Thus, in RStan you never have to deal with templates anymore. You only need your object factory, as discussed, to create instances of specific models (using some key to do so which maps the actual model to the type)… but after creation of the model you would only need to refer to it using a model_base pointer.
The branch is “for_2.20”, which has a bunch of convoluted stuff dealing with a stan::model::model_base* that is instantiated as something that inherits from it.
But I am definitely calling the old templated log_prob_grad function on the dereferenced pointer
This compiles but I don’t know why there isn’t a stub for it in the model_base class. And I still don’t know how to get it to not recompile the model_base class each time users generate C++ code.
Yes, what @wds15 said. For CmdStan, I just compiled statically againt model_base (not model_base_crtp, which is only there to adapt the actual generated model class to implement model_base). Then I compiled the actual model class separately and then linked.
Then that caller has to go in the translation unit with the known model class. That’s how it’s being done with CmdStan.
It’s a pure header, so there’s nothing to compile—it’s just there to provide declarations so things can be compiled against its interface before it’s available.
There are virtual var overloads of log_prob for autodiff. Everything else is an external call that just uses the var instantiations. Such as the functionals like gradient and hessian. log_prob_grad just wraps up the call to the var overload.
It’s a utility function that’s not defined in the model class. That’s why it’s getting the model as an argument.