diff --git a/CMakeLists.txt b/CMakeLists.txt index c530923..da1818c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ option(INTEGRATORXX_HEADER_ONLY "Force header-only build" OFF) if(INTEGRATORXX_HEADER_ONLY) add_library( integratorxx INTERFACE ) set(INTEGRATORXX_TARGET_TYPE INTERFACE) + target_compile_definitions( integratorxx INTERFACE INTEGRATORXX_HEADER_ONLY ) else() add_subdirectory(src) set(INTEGRATORXX_TARGET_TYPE PUBLIC) diff --git a/README.md b/README.md index d416142..9ab0a47 100644 --- a/README.md +++ b/README.md @@ -152,15 +152,16 @@ avoid excessive build times in complex projects with aggressive compiler optimization. **N.B. it is highly recommend that users maintain this default behavior to avoid excessive compilation sizes and build times**. -IntegratorXX also allows for header-only use of the runtime generator by -setting `INTEGRATORXX_HEADER_ONLY=ON`. -This feature also allows for circumvention of -the CMake build system by simply including the requisite implementation -header. - -To use the runtime generator header-only, one needs to include -`` **exactly once** per project, -otherwise duplicate / incompatible symbols will occur. +IntegratorXX also allows for header-only use of the runtime generator by +setting `INTEGRATORXX_HEADER_ONLY=ON`, which defines the +`INTEGRATORXX_HEADER_ONLY` macro on the interface target. The public +generator headers then carry their own implementations, so no additional +include is required. + +To circumvent the CMake build system entirely, define +`INTEGRATORXX_HEADER_ONLY` yourself and include the generator headers as +usual. The implementations have inline linkage in this mode and may be +included from any number of translation units. ## Contributing and Bug Reports diff --git a/include/integratorxx/generators/impl/impl.hpp b/include/integratorxx/generators/impl/impl.hpp index f8b061d..a71c36a 100644 --- a/include/integratorxx/generators/impl/impl.hpp +++ b/include/integratorxx/generators/impl/impl.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include #include diff --git a/include/integratorxx/generators/impl/linkage.hpp b/include/integratorxx/generators/impl/linkage.hpp new file mode 100644 index 0000000..ebb8240 --- /dev/null +++ b/include/integratorxx/generators/impl/linkage.hpp @@ -0,0 +1,18 @@ +#pragma once + +/** + * @brief Linkage of the runtime grid generator. + * + * By default the runtime generator is compiled once into libintegratorxx + * and consumers see only the declarations in generators/*.hpp. This keeps + * the large angular grid dispatch out of every translation unit. + * + * When INTEGRATORXX_HEADER_ONLY is defined the same definitions are emitted + * inline in each translation unit that uses them, and the public generator + * headers pull in their own implementations. + */ +#ifdef INTEGRATORXX_HEADER_ONLY +#define INTEGRATORXX_GENERATOR_LINKAGE inline +#else +#define INTEGRATORXX_GENERATOR_LINKAGE +#endif diff --git a/include/integratorxx/generators/impl/pruned_grid.hpp b/include/integratorxx/generators/impl/pruned_grid.hpp index c69e036..032be58 100644 --- a/include/integratorxx/generators/impl/pruned_grid.hpp +++ b/include/integratorxx/generators/impl/pruned_grid.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -58,7 +59,7 @@ auto make_pruned_grid(const RadialQuadType& rq, } // Implementation Details -SphericalGridFactory::spherical_grid_ptr +INTEGRATORXX_GENERATOR_LINKAGE SphericalGridFactory::spherical_grid_ptr SphericalGridFactory::generate_pruned_grid( RadialQuad rq, const RadialTraits& traits, const std::vector& pruning_regions) { @@ -83,7 +84,7 @@ SphericalGridFactory::spherical_grid_ptr } -PrunedSphericalGridSpecification create_pruned_spec( +INTEGRATORXX_GENERATOR_LINKAGE PrunedSphericalGridSpecification create_pruned_spec( PruningScheme scheme, UnprunedSphericalGridSpecification unp ) { diff --git a/include/integratorxx/generators/impl/radial_factory.hpp b/include/integratorxx/generators/impl/radial_factory.hpp index b1c01a1..e2f45ad 100644 --- a/include/integratorxx/generators/impl/radial_factory.hpp +++ b/include/integratorxx/generators/impl/radial_factory.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -7,7 +8,7 @@ namespace IntegratorXX { -RadialQuad radial_from_string(std::string name) { +INTEGRATORXX_GENERATOR_LINKAGE RadialQuad radial_from_string(std::string name) { std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c){ return static_cast(std::toupper(c)); }); if(name == "BECKE") return RadialQuad::Becke; @@ -21,7 +22,7 @@ RadialQuad radial_from_string(std::string name) { throw std::runtime_error("Unrecognized Radial Quadrature"); } -RadialFactory::radial_grid_ptr RadialFactory::generate(RadialQuad rq, const RadialTraits& traits) { +INTEGRATORXX_GENERATOR_LINKAGE RadialFactory::radial_grid_ptr RadialFactory::generate(RadialQuad rq, const RadialTraits& traits) { switch(rq) { case RadialQuad::Becke: diff --git a/include/integratorxx/generators/impl/robust_pruning.hpp b/include/integratorxx/generators/impl/robust_pruning.hpp index f397fea..3ad7b87 100644 --- a/include/integratorxx/generators/impl/robust_pruning.hpp +++ b/include/integratorxx/generators/impl/robust_pruning.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -24,7 +25,7 @@ auto get_robust_low_med_sizes(AngularSize asz) { } -PrunedSphericalGridSpecification robust_psi4_pruning_scheme_impl( +INTEGRATORXX_GENERATOR_LINKAGE PrunedSphericalGridSpecification robust_psi4_pruning_scheme_impl( size_t low_sz, size_t med_sz, AngularQuad angular_quad, UnprunedSphericalGridSpecification unp ) { @@ -46,7 +47,7 @@ PrunedSphericalGridSpecification robust_psi4_pruning_scheme_impl( } // Implementation Details -PrunedSphericalGridSpecification robust_psi4_pruning_scheme( +INTEGRATORXX_GENERATOR_LINKAGE PrunedSphericalGridSpecification robust_psi4_pruning_scheme( UnprunedSphericalGridSpecification unp ) { size_t low_sz, med_sz; diff --git a/include/integratorxx/generators/impl/s2_factory.hpp b/include/integratorxx/generators/impl/s2_factory.hpp index 3e0363d..7a0d20e 100644 --- a/include/integratorxx/generators/impl/s2_factory.hpp +++ b/include/integratorxx/generators/impl/s2_factory.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -7,7 +8,7 @@ namespace IntegratorXX { -AngularQuad angular_from_string(std::string name) { +INTEGRATORXX_GENERATOR_LINKAGE AngularQuad angular_from_string(std::string name) { std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c){ return static_cast(std::toupper(c)); }); if(name == "AHRENSBEYLKIN") return AngularQuad::AhrensBeylkin; @@ -21,7 +22,7 @@ AngularQuad angular_from_string(std::string name) { throw std::runtime_error("Unrecognized Angular Quadrature"); } -S2Factory::s2_grid_ptr S2Factory::generate(AngularQuad aq, size_t npts) { +INTEGRATORXX_GENERATOR_LINKAGE S2Factory::s2_grid_ptr S2Factory::generate(AngularQuad aq, size_t npts) { switch(aq) { case AngularQuad::AhrensBeylkin: diff --git a/include/integratorxx/generators/impl/treutler_pruning.hpp b/include/integratorxx/generators/impl/treutler_pruning.hpp index d59a107..881cf42 100644 --- a/include/integratorxx/generators/impl/treutler_pruning.hpp +++ b/include/integratorxx/generators/impl/treutler_pruning.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -16,7 +17,7 @@ auto get_treutler_low_med_sizes() { return std::make_pair(low_sz, med_sz); } -PrunedSphericalGridSpecification treutler_pruning_scheme_impl( +INTEGRATORXX_GENERATOR_LINKAGE PrunedSphericalGridSpecification treutler_pruning_scheme_impl( size_t low_sz, size_t med_sz, AngularQuad angular_quad, UnprunedSphericalGridSpecification unp ) { @@ -39,7 +40,7 @@ PrunedSphericalGridSpecification treutler_pruning_scheme_impl( -PrunedSphericalGridSpecification treutler_pruning_scheme( +INTEGRATORXX_GENERATOR_LINKAGE PrunedSphericalGridSpecification treutler_pruning_scheme( UnprunedSphericalGridSpecification unp ) { size_t low_sz, med_sz; diff --git a/include/integratorxx/generators/impl/unpruned_grid.hpp b/include/integratorxx/generators/impl/unpruned_grid.hpp index 758b1fd..9821aec 100644 --- a/include/integratorxx/generators/impl/unpruned_grid.hpp +++ b/include/integratorxx/generators/impl/unpruned_grid.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -37,7 +38,7 @@ auto generate_unpruned_grid_impl(RadialQuad rq, const RadialTraits& traits, } // Implementation details -SphericalGridFactory::spherical_grid_ptr +INTEGRATORXX_GENERATOR_LINKAGE SphericalGridFactory::spherical_grid_ptr SphericalGridFactory::generate_unpruned_grid( RadialQuad rq, const RadialTraits& traits, AngularQuad aq, AngularSize nang) { diff --git a/include/integratorxx/generators/radial_factory.hpp b/include/integratorxx/generators/radial_factory.hpp index 2d4f0ab..428a3c0 100644 --- a/include/integratorxx/generators/radial_factory.hpp +++ b/include/integratorxx/generators/radial_factory.hpp @@ -76,3 +76,10 @@ struct RadialFactory { }; } + +// Header-only builds carry the implementation with the declarations. This is +// included last so that the declarations above are already visible; the impl +// headers include this one back, which #pragma once makes a no-op. +#ifdef INTEGRATORXX_HEADER_ONLY +#include +#endif diff --git a/include/integratorxx/generators/s2_factory.hpp b/include/integratorxx/generators/s2_factory.hpp index 1e550e4..d34953d 100644 --- a/include/integratorxx/generators/s2_factory.hpp +++ b/include/integratorxx/generators/s2_factory.hpp @@ -39,3 +39,10 @@ struct S2Factory { }; } + +// Header-only builds carry the implementation with the declarations. This is +// included last so that the declarations above are already visible; the impl +// headers include this one back, which #pragma once makes a no-op. +#ifdef INTEGRATORXX_HEADER_ONLY +#include +#endif diff --git a/include/integratorxx/generators/spherical_factory.hpp b/include/integratorxx/generators/spherical_factory.hpp index 4937503..8fc2d4d 100644 --- a/include/integratorxx/generators/spherical_factory.hpp +++ b/include/integratorxx/generators/spherical_factory.hpp @@ -188,3 +188,11 @@ struct SphericalGridFactory { }; } + +// Header-only builds carry the implementation with the declarations. This is +// included last so that the declarations above are already visible; the impl +// headers include this one back, which #pragma once makes a no-op. +#ifdef INTEGRATORXX_HEADER_ONLY +#include +#include +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e1a2460..0d32576 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,9 +23,6 @@ else() endif() add_library(integratorxx_common_ut quad_matcher.cxx) -if(INTEGRATORXX_HEADER_ONLY) - target_sources(integratorxx_common_ut PRIVATE lib_impl.cxx) -endif() target_link_libraries( integratorxx_common_ut PUBLIC Catch2::Catch2WithMain integratorxx ) add_executable( quadrature_manipulation quadrature_manipulation.cxx ) diff --git a/test/lib_impl.cxx b/test/lib_impl.cxx deleted file mode 100644 index ba83f11..0000000 --- a/test/lib_impl.cxx +++ /dev/null @@ -1,2 +0,0 @@ -#include -