New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple definitions when including Stan headers in several source files #311
Comments
I'm afraid you're not going to be able to do what you're trying to do. The problem is that all the includes include also statics and other non-inline functions, so they can't have multiple definitions. I don't think this is something we can fix. |
Again, I'm a little new to C++, but isn't it unusual to put function definitions in header files like this? It seems to me that this problem really limits the usefulness of this library. (Unless there is a work-around, I think it would be a deal-breaker for me, since it significantly drives up compile times for complex models.) Why can't the functions be inlined? Most of the ones are giving errors in this example were quite small. |
That’s conventual for much of C++ but things get dramatically more The long compilation times are a serious concern, but at the moment On Jun 30, 2016, at 3:27 PM, Ryan notifications@github.com wrote:
|
The problem with multiple includes seems to be tiny little functions like this one:
If one were to compile these functions separately or inline them, would it really result in such a bloated shared library? Or is there a different problem that I don't see? For some context, I should explain why this is important. I routinely perform inference on models composed of a small number of types of random variables. I'd like to build a library with these distributions and use Stan's autodifferentiation to optimize these models as well as compute derivatives for sensitivity analysis in many different contexts (e.g. this one, and many more if I can get around this difficulty). As-is, because I cannot pre-instantiate instances of my templates with Stan types in a shared library, I have to wait minutes each time I change a single line of code (e.g. tweak a unit test). It's grown quite unusable. In other words, for my own projects I'd be happy to trade bloated shared libraries for faster compile time, but because of a small number of tiny functions in the Stan headers I don't have the option. It seems to me that Stan the modeling language and I could make different decisions about this tradeoff if only the header files were true header files without function definitions. |
It'd be nice if it were just non-templated functions, but We could break things down into header, definition, and Instead, we've gone the with the standard header-only library When we say bloated shared libraries, we mean really bloated.
|
I think there's some miscommunication -- I'm sorry if it's on my part. Other header-only libraries (e.g. Eigen) do not have the problem that I'm describing in Stan. I can do something like First source file:
Second source file:
I can then link both to I'm not suggesting that you guys should instantiate all the Stan functions in your library. My problem is that, in my project which uses Stan, I don't have the option to instantiate them in a shared library. And the reason I can't do that has nothing to do with the size of the Stan objects, but only because there are these tiny non-templated functions like Does this make sense? Again, I'm sorry if I'm missing some basic point. |
A similar issue has been reported previously in stan issue #1149. I'm not sure why it was closed -- maybe for inactivity. To clarify, I've made a simpler example (also in the same repo linked to earlier). Here it is: source1.cpp:
source2.cpp:
These two files cannot be linked without multiple definition errors (unlike other header-only libraries like Eigen). To further clarify, the linline branch of my fork fixes the problem (at least for this simple example). I'm not really asserting that inlining is the answer, but I'm trying to be clear about what the problem is. |
Yeah, we had this linker problem with rstanarm (with |
@bgoodri , up to now that is what I've been doing, too, though with moderately complex projects the compile times have become prohibitively long. |
Even in the best case scenario, the compile times are going to be long. But On Sat, Jul 2, 2016 at 6:56 PM, Ryan notifications@github.com wrote:
|
Yes. Also, at least in my case, I could solve my compile time problems by pre-compiling the relatively limited functionality that I need into a shared library. |
I think the right general solution is to do what we did pure header: foo.hpp We can do that with most of our library and it'll allow The problem is that foo_inst.cpp changes per instantiation, Also, we have some shared global objects that would have I wouldn't mind at all if someone refactored all the code
|
@bob-carpenter , that does seem like it would be a lot of work. But why does all the code have to be rewritten? Things are mostly fine left as templates and inlines, it seems to me that a few small functions were forgotten. For example, why can't the following function (and other similarly small functions) be inlined?
There are some exceptions that are longer, like |
Is every function and template function declared inline I'd be OK inlining everything if it doesn't increase our
|
No, the Eigen headers only define templates and their implementations. AFAIK, they do not instantiate anything. If they did, you could not include Eigen headers in multiple translation units.
Yes, it is an error to instantiate the same template function in two different translation units unless it is declared inline. However, you can define a template function and its implementation in headers that are included in several translation units as long as they are not instantiated. E.g. Ok for headers (definition but not instantiation):
Not ok for headers (instantiation):
Also not ok for headers:
But this is ok for headers:
Did you take a look at my branch? It might clarify things. Can you point me to a benchmark that you all use for measuring compile time changes? |
Thanks. That's how I thought things worked and didn't So we should definitely work on this as it shouldn't hurt Or we can start being better behaved with our non-templated We don't have benchmarks for compilation. We just look
|
Can you try it after adding |
@bgoodri , can you elaborate a little on what you have in mind? I don't see a |
I meant LDFLAGS as it is a linker option |
Why do you say it's not in the cards? Do you have any objection to PR 313, which fixes this problem? |
That PR is fine I guess. I was referring to splitting up the header files into def and inst. |
Yes. I think it is not actually necessary to split header files into def and inst. |
Summary:
Stan headers cause multiple definition errors when included in several source files that are linked together in a library.
Description:
If the Stan headers are included in several source files that are compiled separately and then linked, the compiler fails with multiple definition errors. I am a C++ novice, but I believe this is because non-inline functions are being instantiated in the header files (e.g.
print_mat_size
).Reproducible Steps:
A minimal example to reproduce can be found in this repo. In short, it has two
cpp
files that each instantiate templates using Stan variables. They can be successfully compiled into object files, but the linker fails with multiple definition errors.Current Output:
The linker fails with errors like this:
Expected Output:
A compiled shared library with templates instantiated with Stan types.
Additional Information:
Provide any additional information here.
Current Version:
v2.9.0
The text was updated successfully, but these errors were encountered: