Skip to content

Commit

Permalink
Move TrajectoryFactory initialization to runtime
Browse files Browse the repository at this point in the history
Previous version was relying on "before main global statics
initialization", which prevented Chemharp to be used as a static
library, and might blow my head with static initialization disaster.

This was a long wanted feature \o/
  • Loading branch information
Guillaume Fraux committed Sep 18, 2015
1 parent fe00f56 commit 2e1d522
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 100 deletions.
6 changes: 5 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ option(BUILD_FRONTEND "Build the binary frontend." ON)
option(CODE_COVERAGE "Enable code coverage" OFF)
option(PYTHON_BINDING "Build python interface to Chemharp." OFF)
option(FORTRAN_BINDING "Build FORTRAN interface to Chemharp." OFF)
option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones" OFF)

if(${FORTRAN_BINDING})
enable_language(Fortran)
endif()
if(${PYTHON_BINDING} AND NOT ${BUILD_SHARED_LIBS})
message(SEND_ERROR "The Python binding can not be built using static libs. Please set BUILD_SHARED_LIBS to ON.")
endif()

include(CompilerFlags)
include(Platforms)
Expand Down Expand Up @@ -63,7 +67,7 @@ include_directories(BEFORE SYSTEM ${PROJECT_BINARY_DIR}/include)
include_directories(BEFORE SYSTEM ${PROJECT_SOURCE_DIR}/external/boost)

file(GLOB_RECURSE sources src/**.cpp)
add_library(chemharp SHARED ${sources} bindings/c/capi.cpp)
add_library(chemharp ${sources} bindings/c/capi.cpp)

set_property(TARGET chemharp PROPERTY VERSION ${CHRP_VERSION_SHORT})
set_property(TARGET chemharp PROPERTY SOVERSION ${CHRP_VERSION_SHORT})
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* Rewrite the Python binding to use ctypes. The same code can be used with Python 2 & 3,
and with all numpy versions.
* Easier Python and Julia binding installation, using conda binary packaging.
* Chemharp can now be compiled as a static library! This should allow for easier embedding
in external code, and easier distribution of binaries.

# 0.3

Expand Down
2 changes: 1 addition & 1 deletion bindings/fortran/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fdefault-integer-8")

file(GLOB fortran-sources *.f90)
add_library(chemharpf SHARED ${fortran-sources})
add_library(chemharpf ${fortran-sources})
target_link_libraries(chemharpf chemharp)
set_property(TARGET chemharpf PROPERTY VERSION ${CHRP_VERSION})
set_property(TARGET chemharpf PROPERTY SOVERSION ${CHRP_VERSION})
Expand Down
6 changes: 4 additions & 2 deletions include/chemharp/TrajectoryFactory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ class TrajectoryFactory {
static trajectory_builder_t by_extension(const string& ext);

//! Register a trajectory_builder in the internal format names list.
static bool register_format(const string& name, trajectory_builder_t tb);
static void register_format(const string& name, trajectory_builder_t tb);
//! Register an trajectory_builder in the internal extensions list.
static bool register_extension(const string& ext, trajectory_builder_t tb);
static void register_extension(const string& ext, trajectory_builder_t tb);
//! Are the maps initialized ?
static bool initialized;
};

} // namespace harp
Expand Down
15 changes: 13 additions & 2 deletions include/chemharp/formats/Molfile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class Molfile : public Format {
virtual void read(Frame& frame) override;
virtual std::string description() const override;
virtual size_t nsteps() const override;

static const char* name();
static const char* extension();
private:
/// Convert a molfile timestep to a chemharp frame
void molfile_to_frame(const molfile_timestep_t& timestep, Frame& frame);
Expand All @@ -80,10 +83,18 @@ class Molfile : public Format {
mutable bool _use_topology;
/// Store topological information
mutable Topology _topology;

REGISTER_FORMAT;
};

typedef typename concat<FORMATS_LIST, Molfile<PDB>>::type molfile_list_1;
typedef typename concat<molfile_list_1, Molfile<DCD>>::type molfile_list_2;
typedef typename concat<molfile_list_2, Molfile<GRO>>::type molfile_list_3;
typedef typename concat<molfile_list_3, Molfile<TRR>>::type molfile_list_4;
typedef typename concat<molfile_list_4, Molfile<XTC>>::type molfile_list_5;
typedef typename concat<molfile_list_5, Molfile<TRJ>>::type molfile_list_6;

#undef FORMATS_LIST
#define FORMATS_LIST molfile_list_6

}

#endif
13 changes: 10 additions & 3 deletions include/chemharp/formats/NCFormat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ class NCFormat : public Format {
virtual std::string description() const override;

using file_t = NCFile;

// Register the Amber NetCDF format with the ".nc" extension and the
// "AmberNetCDF" description.
FORMAT_NAME(AmberNetCDF);
FORMAT_EXTENSION(.nc);
private:
//! Reserve size for \c natoms on the internal cache.
void reserve(size_t natoms) const;
Expand All @@ -57,16 +62,18 @@ class NCFormat : public Format {
//! Write an UnitCell to the file, at the current internal step
void write_cell(const UnitCell& cell) const;

//! TODO
//! Reference to the associated file.
NCFile& ncfile;
//! Last read step
size_t step;
// Temporary cache for read and write operations.
mutable std::vector<float> cache;
// Let's register the format
REGISTER_FORMAT;
};

typedef typename concat<FORMATS_LIST, NCFormat>::type FormatListNC;
#undef FORMATS_LIST
#define FORMATS_LIST FormatListNC

} // namespace harp

#endif
Expand Down
9 changes: 8 additions & 1 deletion include/chemharp/formats/XYZ.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,18 @@ class XYZFormat : public Format {
virtual void write(const Frame& frame) override;
virtual std::string description() const override;
virtual size_t nsteps() const override;

// Register the xyz format with the ".xyz" extension and the "XYZ" description.
FORMAT_NAME(XYZ);
FORMAT_EXTENSION(.xyz);
private:
TextFile& textfile;
REGISTER_FORMAT;
};

typedef typename concat<FORMATS_LIST, XYZFormat>::type FormatListXYZ;
#undef FORMATS_LIST
#define FORMATS_LIST FormatListXYZ

} // namespace harp

#endif
64 changes: 40 additions & 24 deletions include/chemharp/register_formats.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,46 @@ unique_ptr<Format> new_format(File& f){
return unique_ptr<Format>(new format_t(f));
}

//! Register a format by associating it to a format name, and no file type.
#define REGISTER(format_t, name) \
bool format_t::_registered_format_ = \
TrajectoryFactory::register_format(name, { \
new_format<format_t>, \
new_file<typename format_t::file_t> \
});

//! Register a format by associating it to an extention, and no file type.
//! The extension should starts with a "."
#define REGISTER_EXTENSION(format_t, extension) \
bool format_t::_registered_extension_ = \
TrajectoryFactory::register_extension(extension, { \
new_format<format_t>, \
new_file<typename format_t::file_t> \
});

//! Add the static members in a class to register a format.
#define REGISTER_FORMAT \
static bool _registered_extension_; \
static bool _registered_format_

#define FORMAT_NAME(x) static constexpr const char name[] = x;
#define FORMAT_EXTENSION(x) static constexpr const char extension[] = x;
// //! Register a format by associating it to a format name, and no file type.
// #define REGISTER(format_t, name) \
// bool format_t::_registered_format_ = \
// TrajectoryFactory::register_format(name, { \
// new_format<format_t>, \
// new_file<typename format_t::file_t> \
// });
//
// //! Register a format by associating it to an extention, and no file type.
// //! The extension should starts with a "."
// #define REGISTER_EXTENSION(format_t, extension) \
// bool format_t::_registered_extension_ = \
// TrajectoryFactory::register_extension(extension, { \
// new_format<format_t>, \
// new_file<typename format_t::file_t> \
// });

#define FORMAT_EXTENSION(x) static const char* extension() {static constexpr char val[] = #x; return val;}
#define FORMAT_NAME(x) static const char* name() {static constexpr char val[] = #x; return val;}

// Create a vector of types at compile time
struct Void {};

template <typename ...> struct concat;

template <template <typename ...> class List, typename T>
struct concat<List<Void>, T>{
typedef List<T> type;
};

template <template <typename ...> class List, typename ...Types, typename T>
struct concat<List<Types...>, T> {
typedef List<Types..., T> type;
};

template <typename...> struct FormatList {};

template <> struct FormatList<Void> {};
typedef FormatList<Void> FormatListVoid;
#define FORMATS_LIST FormatListVoid

} // namespace harp

Expand Down
43 changes: 39 additions & 4 deletions src/TrajectoryFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,37 @@
*/

#include "chemharp/TrajectoryFactory.hpp"


#include "chemharp/formats/XYZ.hpp"
#include "chemharp/formats/NCFormat.hpp"
#include "chemharp/formats/Molfile.hpp"

#include "chemharp/files/NCFile.hpp"
using namespace harp;

typedef FORMATS_LIST formats_list;

template <typename T>
inline void register_all_formats(FormatList<T>) {
auto creator = trajectory_builder_t{new_format<T>, new_file<typename T::file_t>};
if (T::extension() != std::string("")){
TrajectoryFactory::register_extension(T::extension(), creator);
}
if (T::name() != std::string("")){
TrajectoryFactory::register_format(T::name(), creator);
}
}

template <typename T, typename S, typename ...Types>
inline void register_all_formats(FormatList<T, S, Types...>) {
register_all_formats(FormatList<T>());
register_all_formats(FormatList<S, Types...>());
}

// Initializing to false before main.
bool TrajectoryFactory::initialized = false;

trajectory_map_t& TrajectoryFactory::names(){
static auto umap = trajectory_map_t();
return umap;
Expand All @@ -21,30 +50,36 @@ trajectory_map_t& TrajectoryFactory::extensions(){
}

trajectory_builder_t TrajectoryFactory::format(const string& name){
if (!TrajectoryFactory::initialized) {
register_all_formats(formats_list());
TrajectoryFactory::initialized = true;
}
if (names().find(name) == names().end())
throw FormatError("Can not find the format \"" + name + "\".");
return names()[name];
}

trajectory_builder_t TrajectoryFactory::by_extension(const string& ext){
if (!TrajectoryFactory::initialized) {
register_all_formats(formats_list());
TrajectoryFactory::initialized = true;
}
if (extensions().find(ext) == extensions().end())
throw FormatError("Can not find a format associated with the \""
+ ext + "\" extension.");
return extensions()[ext];
}

bool TrajectoryFactory::register_format(const string& name, trajectory_builder_t tb){
void TrajectoryFactory::register_format(const string& name, trajectory_builder_t tb){
if (names().find(name) != names().end())
throw FormatError("The name \"" + name + "\" is already "
"associated with a format.");
names().emplace(name, tb);
return true;
}

bool TrajectoryFactory::register_extension(const string& ext, trajectory_builder_t tb){
void TrajectoryFactory::register_extension(const string& ext, trajectory_builder_t tb){
if (extensions().find(ext) != extensions().end())
throw FormatError("The extension \"" + ext + "\" is already "
"associated with a format.");
extensions().emplace(ext, tb);
return true;
}
62 changes: 18 additions & 44 deletions src/formats/Molfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ struct plugin_data_t {
std::string format;
std::string path;
std::string plugin_name;
std::string extension;
bool have_velocities;
};

static std::map<MolfileFormat, plugin_data_t> molfile_plugins {
{PDB, {"PDB", "pdbplugin.so", "pdb", false}},
{DCD, {"DCD", "dcdplugin.so", "dcd", false}},
{GRO, {"GRO", "gromacsplugin.so", "gro", false}},
{TRR, {"TRR", "gromacsplugin.so", "trr", true}},
{XTC, {"XTC", "gromacsplugin.so", "xtc", false}},
{TRJ, {"Gromacs trj", "gromacsplugin.so", "trj", true}},
{PDB, {"PDB", "pdbplugin.so", "pdb", ".pdb", false}},
{DCD, {"DCD", "dcdplugin.so", "dcd", ".dcd", false}},
{GRO, {"GRO", "gromacsplugin.so", "gro", ".gro", false}},
{TRR, {"TRR", "gromacsplugin.so", "trr", ".trr", true}},
{XTC, {"XTC", "gromacsplugin.so", "xtc", ".xtc", false}},
{TRJ, {"Gromacs trj", "gromacsplugin.so", "trj", ".trj", true}},
};

struct plugin_reginfo_t {
Expand Down Expand Up @@ -244,49 +245,22 @@ void Molfile<F>::read_topology() const {
_topology.recalculate();
}

template <MolfileFormat F> const char* Molfile<F>::name() {
static const char* val = molfile_plugins[F].format.c_str();
return val;
}

template <MolfileFormat F> const char* Molfile<F>::extension() {
static const char* val = molfile_plugins[F].extension.c_str();
return val;
}

/******************************************************************************/

// Instanciate the templates
template class harp::Molfile<PDB>;
template class harp::Molfile<DCD>;
template class harp::Molfile<GRO>;
template class harp::Molfile<TRR>;
template class harp::Molfile<XTC>;
template class harp::Molfile<TRJ>;

// Redefine the registering macros
#undef REGISTER
#undef REGISTER_EXTENSION

#define REGISTER(format_t, name) \
template<> bool format_t::_registered_format_ = \
TrajectoryFactory::register_format(name, { \
new_format<format_t>, \
new_file<typename format_t::file_t> \
});

#define REGISTER_EXTENSION(format_t, extension) \
template<> bool format_t::_registered_extension_ = \
TrajectoryFactory::register_extension(extension, { \
new_format<format_t>, \
new_file<typename format_t::file_t> \
});

/******************************************************************************/

REGISTER(Molfile<PDB>, "PDB");
REGISTER_EXTENSION(Molfile<PDB>, ".pdb");

REGISTER(Molfile<DCD>, "DCD");
REGISTER_EXTENSION(Molfile<DCD>, ".dcd");

REGISTER(Molfile<GRO>, "GRO");
REGISTER_EXTENSION(Molfile<GRO>, ".gro");

REGISTER(Molfile<TRR>, "TRR");
REGISTER_EXTENSION(Molfile<TRR>, ".trr");

REGISTER(Molfile<XTC>, "XTC");
REGISTER_EXTENSION(Molfile<XTC>, ".xtc");

REGISTER(Molfile<TRJ>, "Gromacs trj");
REGISTER_EXTENSION(Molfile<TRJ>, ".trj");
5 changes: 0 additions & 5 deletions src/formats/NCFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,4 @@ void NCFormat::write_cell(const UnitCell& cell) const {
angles.putVar(start, count, angles_data);
}

// Register the Amber NetCDF format with the ".nc" extension and the
// "AmberNetCDF" description.
REGISTER(NCFormat, "AmberNetCDF");
REGISTER_EXTENSION(NCFormat, ".nc");

#endif // HAVE_NETCDF
5 changes: 0 additions & 5 deletions src/formats/XYZ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,3 @@ void XYZFormat::write(const Frame& frame){
<< pos[0] << " " << pos[1] << " " << pos[2] << "\n";
}
}


// Register the xyz format with the ".xyz" extension and the "XYZ" description.
REGISTER(XYZFormat, "XYZ");
REGISTER_EXTENSION(XYZFormat, ".xyz");
Loading

0 comments on commit 2e1d522

Please sign in to comment.