22 Using external C++ code
The --allow-undefined
flag can be passed to the call to stanc,
which will allow undefined functions in the Stan language to be parsed
without an error. We can then include a definition of the function in
a C++ header file.
This requires specifying two makefile variables:
- STANCFLAGS=--allow-undefined
- USER_HEADER=<header_file.hpp>
, where <header_file.hpp>
is the name of a header file that
defines a function with the same name and signature in a namespace
that is formed by concatenating the class_name
argument to
stanc documented above to the string _namespace
As an example, consider the following variant of the Bernoulli example
functions {
real make_odds(real theta);
}data {
int<lower=0> N;
array[N] int<lower=0, upper=1> y;
}parameters {
real<lower=0, upper=1> theta;
}model {
1, 1); // uniform prior on interval 0, 1
theta ~ beta(
y ~ bernoulli(theta);
}generated quantities {
real odds;
odds = make_odds(theta); }
Here the make_odds
function is declared but not defined, which
would ordinarily result in a parser error. However, if you put
STANCFLAGS = --allow-undefined
into the make/local
file
or into the stanc
call, then the stanc compiler will translate
this program to C++, but the generated C++ code will not compile
unless you write a file
such as examples/bernoulli/make_odds.hpp
with the following lines
#include <stan/model/model_header.hpp>
#include <ostream>
namespace bernoulli_model_namespace {
template <typename T0__,
::require_all_t<stan::is_stan_scalar<T0__>>* = nullptr>
stan::promote_args_t<T0__>
stan(const T0__& theta, std::ostream* pstream__) {
make_oddsreturn theta / (1 - theta);
}
}
The signature for this function needs to match the declaration emitted by stanc.
One can inspect the generated .hpp
file by stanc to determine what is needed.
Given the above, the following make
invocation should work
> make STANCFLAGS=--allow-undefined USER_HEADER=examples/bernoulli/make_odds.hpp examples/bernoulli/bernoulli # on Windows add .exe
Alternatively, you could put STANCFLAGS
and USER_HEADER
into the
make/local
file instead of specifying them on the command-line.
If the function were more complicated and involved functions in the
Stan Math Library, then you would need to prefix the function calls
with stan::math::
The pstream__
argument is mandatory
in the signature but need not be used if your function does not print
any output. To see the necessary boilerplate look at the corresponding
lines in the generated C++ file.
For more details about how to write C++ code using the Stan Math Library, see the Math library documentation at https://mc-stan.org/math/ or the paper at https://arxiv.org/abs/1509.07164.