From 8e1c4b53b808ebdc828a00266bc535c1ce32e78d Mon Sep 17 00:00:00 2001 From: Marko Budiselic <mbudiselicbuda@gmail.com> Date: Thu, 22 Oct 2015 00:17:40 +0200 Subject: [PATCH 1/3] cpp program runs compile command, test only code --- .gitignore | 1 + dc/README.md | 1 + dc/test.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ dc/tmp/.gitignore | 2 ++ 4 files changed, 50 insertions(+) create mode 100644 dc/README.md create mode 100644 dc/test.cpp create mode 100644 dc/tmp/.gitignore diff --git a/.gitignore b/.gitignore index 81a453937..1eff11582 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ *.swp *.swo *.out +*.so *.dSYM/ memgraph diff --git a/dc/README.md b/dc/README.md new file mode 100644 index 000000000..2bf28fe09 --- /dev/null +++ b/dc/README.md @@ -0,0 +1 @@ +## Dynamic Code diff --git a/dc/test.cpp b/dc/test.cpp new file mode 100644 index 000000000..685aa8cde --- /dev/null +++ b/dc/test.cpp @@ -0,0 +1,46 @@ +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <iterator> +#include <cstdlib> + +// TODO: clang++ file.cpp -o file.so -shared -fPIC + +using namespace std; + +void write(const std::string& path, const std::string& content) +{ + ofstream stream; + stream.open (path.c_str()); + stream << content; + stream.close(); +} + +std::string join(const std::vector<std::string>& strings, const char *separator) +{ + std::ostringstream oss; + std::copy(strings.begin(), strings.end(), + std::ostream_iterator<std::string>(oss, separator)); + return oss.str(); +} + +template<typename... Args> +std::string prints(const Args&... args) +{ + std::vector<std::string> strings = {args...}; + return join(strings, " "); +} + +int main() +{ + string tmp_file_path = "tmp/tmp.cpp"; + string tmp_so_path = "tmp/tmp.so"; + string for_compile = "#include <iostream>\nint main() { std::cout << \"test\" << std::endl; return 0; }"; + + write(tmp_file_path, for_compile); + string test_command = prints("clang++", tmp_file_path, "-o", "test.out"); + system(test_command.c_str()); + + return 0; +} diff --git a/dc/tmp/.gitignore b/dc/tmp/.gitignore new file mode 100644 index 000000000..78d910160 --- /dev/null +++ b/dc/tmp/.gitignore @@ -0,0 +1,2 @@ +/* +!.gitignore From 951448f6b2cd0effb4b45a02c02908b75daec3d7 Mon Sep 17 00:00:00 2001 From: Marko Budiselic <mbudiselicbuda@gmail.com> Date: Thu, 22 Oct 2015 23:54:28 +0200 Subject: [PATCH 2/3] dynamic lib load work in progress :P --- dc/README.md | 6 +++++ dc/example/db.hpp | 19 +++++++++++++++ dc/example/memsql.cpp | 28 ++++++++++++++++++++++ dc/example/mysql.cpp | 28 ++++++++++++++++++++++ dc/example/neo4j.cpp | 28 ++++++++++++++++++++++ dc/example/postgresql.cpp | 28 ++++++++++++++++++++++ dc/test.cpp | 50 ++++++++++++++++++++++++++++++++++----- 7 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 dc/example/db.hpp create mode 100644 dc/example/memsql.cpp create mode 100644 dc/example/mysql.cpp create mode 100644 dc/example/neo4j.cpp create mode 100644 dc/example/postgresql.cpp diff --git a/dc/README.md b/dc/README.md index 2bf28fe09..116d58797 100644 --- a/dc/README.md +++ b/dc/README.md @@ -1 +1,7 @@ ## Dynamic Code + +``` +cd example +clang++ -std=c++1y mysql.cpp -o ../tmp/mysql.so -shared -fPIC +clang++ -std=c++1y test.cpp -o test.out -ldl +``` diff --git a/dc/example/db.hpp b/dc/example/db.hpp new file mode 100644 index 000000000..a507968df --- /dev/null +++ b/dc/example/db.hpp @@ -0,0 +1,19 @@ +#ifndef MEMGRAPH_DL_EXAMPLE_DB_HPP +#define MEMGRAPH_DL_EXAMPLE_DB_HPP + +#include <iostream> + +using namespace std; + +class db +{ +public: + virtual void name() const; + virtual void type() const; + virtual ~db() {} +}; + +typedef db* (*produce_t)(); +typedef void (*destruct_t)(db*); + +#endif diff --git a/dc/example/memsql.cpp b/dc/example/memsql.cpp new file mode 100644 index 000000000..b8fa37ef4 --- /dev/null +++ b/dc/example/memsql.cpp @@ -0,0 +1,28 @@ +#include "db.hpp" + +class memsql : public db +{ +public: + + void name() const override + { + cout << "MemSQL" << endl; + } + + void type() const override + { + cout << "InMemory" << endl; + } + + ~memsql() {} +}; + +extern "C" db* produce() +{ + return new memsql(); +} + +extern "C" void destruct(db* p) +{ + delete p; +} diff --git a/dc/example/mysql.cpp b/dc/example/mysql.cpp new file mode 100644 index 000000000..3b6c1bb2c --- /dev/null +++ b/dc/example/mysql.cpp @@ -0,0 +1,28 @@ +#include "db.hpp" + +class mysql : public db +{ +public: + + virtual void name() const + { + cout << "MySQL" << endl; + } + + virtual void type() const + { + cout << "Relational" << endl; + } + + ~mysql() {} +}; + +extern "C" db* produce() +{ + return new mysql(); +} + +extern "C" void destruct(db* p) +{ + delete p; +} diff --git a/dc/example/neo4j.cpp b/dc/example/neo4j.cpp new file mode 100644 index 000000000..0418bed74 --- /dev/null +++ b/dc/example/neo4j.cpp @@ -0,0 +1,28 @@ +#include "db.hpp" + +class neo4j : public db +{ +public: + + void name() const override + { + cout << "Neo4j" << endl; + } + + void type() const override + { + cout << "Graph" << endl; + } + + ~neo4j() {} +}; + +extern "C" db* produce() +{ + return new neo4j(); +} + +extern "C" void destruct(db* p) +{ + delete p; +} diff --git a/dc/example/postgresql.cpp b/dc/example/postgresql.cpp new file mode 100644 index 000000000..34b5445fe --- /dev/null +++ b/dc/example/postgresql.cpp @@ -0,0 +1,28 @@ +#include "db.hpp" + +class postgresql : public db +{ +public: + + void name() const override + { + cout << "PostgreSQL" << endl; + } + + void type() const override + { + cout << "Relational" << endl; + } + + ~postgresql() {} +}; + +extern "C" db* produce() +{ + return new postgresql(); +} + +extern "C" void destruct(db* p) +{ + delete p; +} diff --git a/dc/test.cpp b/dc/test.cpp index 685aa8cde..ba582ad92 100644 --- a/dc/test.cpp +++ b/dc/test.cpp @@ -4,6 +4,8 @@ #include <vector> #include <iterator> #include <cstdlib> +#include <dlfcn.h> +#include "example/db.hpp" // TODO: clang++ file.cpp -o file.so -shared -fPIC @@ -32,15 +34,51 @@ std::string prints(const Args&... args) return join(strings, " "); } +db* database(const std::string& lib_path, const std::string& factory_method) +{ + // load lib + void* db_lib = dlopen(lib_path.c_str(), RTLD_LAZY); + if (!db_lib) { + cerr << "Cannot load library: " << dlerror() << '\n'; + return nullptr; + } + dlerror(); + + // load produce method + produce_t produce_db = (produce_t) dlsym(db_lib, factory_method.c_str()); + const char* dlsym_error = dlerror(); + if (dlsym_error) { + cerr << "Cannot load symbol create: " << dlsym_error << '\n'; + return nullptr; + } + + // load destroy method + // destruct_t destruct_db = (destruct_t) dlsym(db_lib, "destruct"); + // dlsym_error = dlerror(); + // if (dlsym_error) { + // cerr << "Cannot load symbol destroy: " << dlsym_error << '\n'; + // return nullptr; + // } + + db *instance = produce_db(); + + return instance; +} + int main() { - string tmp_file_path = "tmp/tmp.cpp"; - string tmp_so_path = "tmp/tmp.so"; - string for_compile = "#include <iostream>\nint main() { std::cout << \"test\" << std::endl; return 0; }"; + // string tmp_file_path = "tmp/tmp.cpp"; + // string tmp_so_path = "tmp/tmp.so"; + // string for_compile = "#include <iostream>\nint main() { std::cout << \"test\" << std::endl; return 0; }"; - write(tmp_file_path, for_compile); - string test_command = prints("clang++", tmp_file_path, "-o", "test.out"); - system(test_command.c_str()); + // write(tmp_file_path, for_compile); + // string test_command = prints("clang++", tmp_file_path, "-o", "test.out"); + // system(test_command.c_str()); + + db *mysql = database("./tmp/mysql.so", "produce"); + if (mysql) { + mysql->name(); + } return 0; } From 120743e59c4dab339de499ce3090203517437e35 Mon Sep 17 00:00:00 2001 From: Marko Budiselic <mbudiselicbuda@gmail.com> Date: Sun, 25 Oct 2015 16:11:52 +0100 Subject: [PATCH 3/3] Much better but not perfect version of dynamic compile and dynamic load code. DynamicLib class and rudimentar example. --- dc/README.md | 5 ++++ dc/dynamic_lib.hpp | 65 ++++++++++++++++++++++++++++++++++++++++++++ dc/example/db.hpp | 10 +++++-- dc/test.cpp | 68 +++++++++++++++++++++++----------------------- 4 files changed, 112 insertions(+), 36 deletions(-) create mode 100644 dc/dynamic_lib.hpp diff --git a/dc/README.md b/dc/README.md index 116d58797..a4ba9a8ac 100644 --- a/dc/README.md +++ b/dc/README.md @@ -1,7 +1,12 @@ ## Dynamic Code ``` +man nm + +cd memgraph/dc cd example clang++ -std=c++1y mysql.cpp -o ../tmp/mysql.so -shared -fPIC +clang++ -std=c++1y memsql.cpp -o ../tmp/memsql.so -shared -fPIC +cd .. clang++ -std=c++1y test.cpp -o test.out -ldl ``` diff --git a/dc/dynamic_lib.hpp b/dc/dynamic_lib.hpp new file mode 100644 index 000000000..d7b96e2e1 --- /dev/null +++ b/dc/dynamic_lib.hpp @@ -0,0 +1,65 @@ +#ifndef MEMGRAPH_DL_DYNAMIC_LIB_HPP +#define MEMGRAPH_DL_DYNAMIC_LIB_HPP + +#include <string> +#include <stdexcept> +#include <dlfcn.h> + +template<typename T> +class DynamicLib +{ +public: + typename T::produce produce_method; + typename T::destruct destruct_method; + + DynamicLib(const std::string& lib_path) : + lib_path(lib_path) + { + } + + void load() + { + load_lib(); + load_produce_func(); + load_destruct_func(); + } + +private: + std::string lib_path; + void *dynamic_lib; + + void load_lib() + { + dynamic_lib = dlopen(lib_path.c_str(), RTLD_LAZY); + if (!dynamic_lib) { + throw std::runtime_error(dlerror()); + } + dlerror(); + } + + void load_produce_func() + { + produce_method = (typename T::produce) dlsym( + dynamic_lib, + T::produce_name.c_str() + ); + const char* dlsym_error = dlerror(); + if (dlsym_error) { + throw std::runtime_error(dlsym_error); + } + } + + void load_destruct_func() + { + destruct_method = (typename T::destruct) dlsym( + dynamic_lib, + T::destruct_name.c_str() + ); + const char *dlsym_error = dlerror(); + if (dlsym_error) { + throw std::runtime_error(dlsym_error); + } + } +}; + +#endif diff --git a/dc/example/db.hpp b/dc/example/db.hpp index a507968df..2c93e4c66 100644 --- a/dc/example/db.hpp +++ b/dc/example/db.hpp @@ -8,8 +8,14 @@ using namespace std; class db { public: - virtual void name() const; - virtual void type() const; + // If virtual methods don't have = 0 the compiler + // won't create appropriate _ZTI symbol inside + // the .so lib. That will lead to undefined symbol + // error while the library is loading. + // + // TODO: why? + virtual void name() const = 0; + virtual void type() const = 0; virtual ~db() {} }; diff --git a/dc/test.cpp b/dc/test.cpp index ba582ad92..d40c2ddd3 100644 --- a/dc/test.cpp +++ b/dc/test.cpp @@ -4,12 +4,11 @@ #include <vector> #include <iterator> #include <cstdlib> -#include <dlfcn.h> #include "example/db.hpp" +#include "dynamic_lib.hpp" -// TODO: clang++ file.cpp -o file.so -shared -fPIC - -using namespace std; +using std::cout; +using std::endl; void write(const std::string& path, const std::string& content) { @@ -34,39 +33,24 @@ std::string prints(const Args&... args) return join(strings, " "); } -db* database(const std::string& lib_path, const std::string& factory_method) +// dependent on specific dynamic code +// "configuration" of DynamicLib +// DynamicLib<MemgraphDynamicLib> +class MemgraphDynamicLib { - // load lib - void* db_lib = dlopen(lib_path.c_str(), RTLD_LAZY); - if (!db_lib) { - cerr << "Cannot load library: " << dlerror() << '\n'; - return nullptr; - } - dlerror(); - - // load produce method - produce_t produce_db = (produce_t) dlsym(db_lib, factory_method.c_str()); - const char* dlsym_error = dlerror(); - if (dlsym_error) { - cerr << "Cannot load symbol create: " << dlsym_error << '\n'; - return nullptr; - } - - // load destroy method - // destruct_t destruct_db = (destruct_t) dlsym(db_lib, "destruct"); - // dlsym_error = dlerror(); - // if (dlsym_error) { - // cerr << "Cannot load symbol destroy: " << dlsym_error << '\n'; - // return nullptr; - // } - - db *instance = produce_db(); - - return instance; -} +public: + const static std::string produce_name; + const static std::string destruct_name; + typedef produce_t produce; + typedef destruct_t destruct; +}; +const std::string MemgraphDynamicLib::produce_name = "produce"; +const std::string MemgraphDynamicLib::destruct_name = "destruct"; int main() { + // -- compile example + // string tmp_file_path = "tmp/tmp.cpp"; // string tmp_so_path = "tmp/tmp.so"; // string for_compile = "#include <iostream>\nint main() { std::cout << \"test\" << std::endl; return 0; }"; @@ -74,11 +58,27 @@ int main() // write(tmp_file_path, for_compile); // string test_command = prints("clang++", tmp_file_path, "-o", "test.out"); // system(test_command.c_str()); + + // -- end compile example - db *mysql = database("./tmp/mysql.so", "produce"); + // -- load example + using db_lib = DynamicLib<MemgraphDynamicLib>; + + db_lib mysql_db("./tmp/mysql.so"); + mysql_db.load(); + auto mysql = mysql_db.produce_method(); if (mysql) { mysql->name(); } + mysql_db.destruct_method(mysql); + + db_lib memsql_db("./tmp/memsql.so"); + memsql_db.load(); + auto memsql = memsql_db.produce_method(); + if (memsql) { + memsql->name(); + } + memsql_db.destruct_method(memsql); return 0; }