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;
 }