Compilation error with external C++

I am trying to compile the basic Bernoulli model with an external C++ function.
I have just downloaded the latest CmdStan from GitHub.
Added make_odds.hpp with the following content:

namespace bernoulli_model_namespace {

    template <typename T0__>
    stan::promote_args_t<T0__>
    make_odds(const T0__& theta, std::ostream* pstream__) {
        return theta / (1 - theta);
    };

}

and modified bernoulli.stan:

functions {
  real make_odds(real theta);
}
data { 
  int<lower=0> N; 
  int<lower=0,upper=1> y[N];
} 
parameters {
  real<lower=0,upper=1> theta;
  real theta2;
} 
model {
  real out = make_odds(theta2); // ADDED THIS LINE
  theta ~ beta(1,1);  // uniform prior on interval 0,1
  y ~ bernoulli(theta);
}

Now I’m trying to compile the model with USER_HEADER=examples/bernoulli/make_odds.hpp STANCFLAGS=--allow-undefined make examples/bernoulli/bernoulli and I get the error

Undefined symbols for architecture x86_64:
  "boost::math::tools::promote_args<stan::math::var, float, float, float, float, float>::type bernoulli_model_namespace::make_odds<stan::math::var>(stan::math::var const&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*)", referenced from:
      stan::math::var bernoulli_model_namespace::bernoulli_model::log_prob<false, false, stan::math::var>(std::__1::vector<stan::math::var, std::__1::allocator<stan::math::var> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
      stan::math::var bernoulli_model_namespace::bernoulli_model::log_prob<false, true, stan::math::var>(std::__1::vector<stan::math::var, std::__1::allocator<stan::math::var> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
      stan::math::var bernoulli_model_namespace::bernoulli_model::log_prob<true, false, stan::math::var>(std::__1::vector<stan::math::var, std::__1::allocator<stan::math::var> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
      stan::math::var bernoulli_model_namespace::bernoulli_model::log_prob<true, true, stan::math::var>(std::__1::vector<stan::math::var, std::__1::allocator<stan::math::var> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
  "boost::math::tools::promote_args<double, float, float, float, float, float>::type bernoulli_model_namespace::make_odds<double>(double const&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*)", referenced from:
      double bernoulli_model_namespace::bernoulli_model::log_prob<false, false, double>(std::__1::vector<double, std::__1::allocator<double> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
      double bernoulli_model_namespace::bernoulli_model::log_prob<false, true, double>(std::__1::vector<double, std::__1::allocator<double> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
      double bernoulli_model_namespace::bernoulli_model::log_prob<true, false, double>(std::__1::vector<double, std::__1::allocator<double> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
      double bernoulli_model_namespace::bernoulli_model::log_prob<true, true, double>(std::__1::vector<double, std::__1::allocator<double> >&, std::__1::vector<int, std::__1::allocator<int> >&, std::__1::basic_ostream<char, std::__1::char_traits<char> >*) const in bernoulli.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [examples/bernoulli/bernoulli] Error 1

What has changed now with how external headers are handled? It was working before.
What am I doing wrong?

You have to still use --allow_undefined for the USER_HEADER to get picked up (will make an issue) or add

CXXFLAGS_PROGRAM += -include $(USER_HEADER)

to the make/local.

I’ve tried --allow_undefined as well. I get a different error in this case

In file included from <built-in>:2:
./examples/bernoulli/make_odds.hpp:5:5: error: redefinition of 'make_odds'
    make_odds(const T0__& theta, std::ostream* pstream__) {
    ^
./examples/bernoulli/make_odds.hpp:5:5: note: previous definition is here
    make_odds(const T0__& theta, std::ostream* pstream__) {
    ^
examples/bernoulli/bernoulli.hpp:196:15: error: no matching function for call to
      'make_odds'
        out = make_odds(theta2, pstream__);

Adding CXXFLAGS_PROGRAM += -include $(USER_HEADER) with --allow-undefined works!

Sorry to reopen an old thread but I am having similar issues with compiling external C++ in cmdstan. I have followed the steps in this thread and the documentation (https://mc-stan.org/docs/2_27/cmdstan-guide/stanc.html#using-external-c-code) but am getting compile errors.

I have followed @IvanYashchuk and have in make_odds.hpp

namespace bernoulli_model_namespace {

    template <typename T0__>
    stan::promote_args_t<T0__>
    make_odds(const T0__& theta, std::ostream* pstream__) {
        return theta / (1 - theta);
    };

}

and the same modified bernoulli.stan,

functions {
  real make_odds(real theta);
}
data { 
  int<lower=0> N; 
  int<lower=0,upper=1> y[N];
} 
parameters {
  real<lower=0,upper=1> theta;
  real theta2;
} 
model {
  real out = make_odds(theta2); // ADDED THIS LINE
  theta ~ beta(1,1);  // uniform prior on interval 0,1
  y ~ bernoulli(theta);
}

then in make/local:

USER_HEADER=examples/bernoulli/make_odds.hpp
STANCFLAGS=--allow-undefined
CXXFLAGS_PROGRAM += -include $(USER_HEADER)

$(info LOCAL MAKE INCLUDED)
$(info USER_HEADER     : $(USER_HEADER))
$(info STANCFLAGS      : $(STANCFLAGS))
$(info CXXFLAGS_PROGRAM: $(CXXFLAGS_PROGRAM))

On compile, I get the following error:

LOCAL MAKE INCLUDED
USER_HEADER     : examples/bernoulli/make_odds.hpp
STANCFLAGS      : --allow-undefined
CXXFLAGS_PROGRAM: -include examples/bernoulli/make_odds.hpp

--- Translating Stan model to C++ code ---
bin/stanc --allow-undefined --o=examples/bernoulli/bernoulli.hpp examples/bernoulli/bernoulli.stan

--- Compiling, linking C++ code ---
g++ -std=c++1y -pthread -D_REENTRANT -Wno-sign-compare -Wno-ignored-attributes      -I stan/lib/stan_math/lib/tbb_2020.3/include    -O3 -I src -I stan/src -I lib/rapidjson_1.1.0/ -I lib/CLI11-1.9.1/ -I stan/lib/stan_math/ -I stan/lib/stan_math/lib/eigen_3.3.9 -I stan/lib/stan_math/lib/boost_1.75.0 -I stan/lib/stan_math/lib/sundials_5.7.0/include    -DBOOST_DISABLE_ASSERTS          -c -include examples/bernoulli/make_odds.hpp -Wno-ignored-attributes   -include examples/bernoulli/make_odds.hpp -include examples/bernoulli/make_odds.hpp -x c++ -o examples/bernoulli/bernoulli.o examples/bernoulli/bernoulli.hpp
In file included from <command-line>:
./examples/bernoulli/make_odds.hpp:4:5: error: ‘stan’ does not name a type
    4 |     stan::promote_args_t<T0__>
      |     ^~~~
In file included from <command-line>:
./examples/bernoulli/make_odds.hpp:4:5: error: ‘stan’ does not name a type
    4 |     stan::promote_args_t<T0__>
      |     ^~~~
In file included from <command-line>:
./examples/bernoulli/make_odds.hpp:4:5: error: ‘stan’ does not name a type
    4 |     stan::promote_args_t<T0__>
      |     ^~~~
make: *** [make/program:55: examples/bernoulli/bernoulli] Error 1

Stan (cmdstan 2.27.0) is working perfectly when not using external C++ files and I couldn’t find this issue mentioned elsewhere.

Apologies if the fix is something obvious, I assume I am missing something in the includes and would be very grateful for any help.