diff --git a/_posts/2023-10-20-C++-Common-Errors-And-Solutions.markdown b/_posts/2023-10-20-C++-Common-Errors-And-Solutions.markdown index cf4c063..4f5a74d 100644 --- a/_posts/2023-10-20-C++-Common-Errors-And-Solutions.markdown +++ b/_posts/2023-10-20-C++-Common-Errors-And-Solutions.markdown @@ -142,3 +142,315 @@ private: In this way, lib.so only exposes an Interface to public. External users are unaware of the implementation of the interface, lib.so does not have to expose any symbols either. This is the 2nd use case of interface to show base class cannot work here (the 1st use case is in Unit Test where interface is used as parameter for the test to mock/stub) + +(Use a more concret example to illuastrate) + +## Solution 1, wrapper solution: + +```cpp +// makefile. libcustom_ep.so is the plugin of libort.so +libort.so:graph_viewer.cpp graph_viewer_ref.cpp session.cpp + g++ -g graph_viewer.cpp graph_viewer_ref.cpp session.cpp -shared -fPIC -o libort.so + +libcustom_ep.so:custom_ep.cpp + g++ -g custom_ep.cpp -shared -fPIC -o libcustom_ep.so + +test.out:test_wrapper.cpp libort.so + g++ -g test_wrapper.cpp -L. -lort -o test.out + +// graph_viewer.h +#pragma once +#include + +class GraphViewer { +public: + GraphViewer() {} + ~GraphViewer() {} + bool IsConstantInitializer(std::string& name, bool check_outer_scope); +}; + +// graph_viewer.cpp +#include "graph_viewer.h" + +bool GraphViewer::IsConstantInitializer(std::string& name, bool check_outer_scope) { + if (check_outer_scope && name == "name") return true; + return false; +} + +// graph_viewer_ref.h +#pragma once +#include + +class GraphViewer; + +class GraphViewerRef { +public: +GraphViewerRef(GraphViewer* g) : graph_(g) {} +~GraphViewerRef() = default; +bool IsConstantInitializer(std::string& name, bool check_outer_scope); +private: +GraphViewer* graph_; +}; + +// graph_viewer_ref.cpp +#include "graph_viewer_ref.h" +#include "graph_viewer.h" + +bool GraphViewerRef::IsConstantInitializer(std::string& name, bool check_outer_scope) { + return graph_->IsConstantInitializer(name, check_outer_scope); +} + +// session.h +#pragma once +#include +class EPAdapter; + +class session { +public: + session() {} + ~session(); + void RegisterCustomEP(std::string path); + void Run(); +private: + EPAdapter* ep_adapter_; +}; + +// session.cpp +#include +#include +#include "session.h" +#include "provider.h" +#include "graph_viewer.h" + +class EPAdapter { +public: + EPAdapter(ExecutionProvider* ep) : ep_(ep) {} + int GetCapability(GraphViewerRef* graph) { return ep_->GetCapability(graph); } +private: + ExecutionProvider* ep_; +}; + +void session::RegisterCustomEP(std::string path) { + void* handle = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL); + ExecutionProvider* (*GetExternalProvider)(); + *(void **) (&GetExternalProvider) = dlsym(handle, "GetExternalProvider"); + ep_adapter_ = new EPAdapter((*GetExternalProvider)()); +} + +void session::Run() { + std::cout << "session::Run()\n"; + GraphViewer* graph = new GraphViewer(); + GraphViewerRef graph_ref(graph); + std::cout << "ep graph get capabiliyty: "<GetCapability(&graph_ref)<<"\n"; + delete graph; +} + +session::~session() { + delete ep_adapter_; +} + +// provider.h, API ExecutionProvider provided by libort.so. Caller needs to reimplement GetCapability() with API GraphViewerRef +#pragma once +#include "graph_viewer_ref.h" + +class ExecutionProvider { +public: + ExecutionProvider() {}; + virtual ~ExecutionProvider() = default; + + virtual int GetCapability(GraphViewerRef*) { return 0; } +}; + +// custom_ep.h +#pragma once +#include "provider.h" + +class custom_ep : public ExecutionProvider { +public: + custom_ep(std::string& n); + ~custom_ep() = default; + int GetCapability(GraphViewerRef*) override; +private: + std::string name; +}; + +// custom_ep.cpp +#include "custom_ep.h" + +custom_ep::custom_ep(std::string& n) { name = n; } + +int custom_ep::GetCapability(GraphViewerRef* graph) { + if (graph->IsConstantInitializer(name, true)) return 1; + return 5; +} + +#ifdef __cplusplus +extern "C" { +#endif + +custom_ep* GetExternalProvider() { + std::string name = "custom_ep"; + return new custom_ep(name); +} + +#ifdef __cplusplus +} +#endif + +// test_wrapper.cpp +#include "session.h" + +int main() { + session sess; + sess.RegisterCustomEP("/home/leca/code/plugin2/libcustom_ep.so"); + sess.Run(); + return 0; +} + +``` + +In this way, the symbol of the function GraphViewerRef::IsConstantInitializer() is in custom\_ep.so: + +```bash +leca@host [ ~/code/plugin2 ]$ readelf -s --wide libcustom_ep.so | grep -i 'IsConstantInitializer' + 28: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZN14GraphViewerRef21IsConstantInitializerERNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEb + 76: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _ZN14GraphViewerRef21IsConstantInitializerERNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEb +``` + +## Solution 2, pure virtual class solution: + +```cpp +// makefile +libort.so:graph_view_api_impl.cpp session.cpp + g++ -g graph_view_api_impl.cpp session.cpp -shared -fPIC -o libort.so + +libcustom_ep.so:custom_ep.cpp + g++ -g custom_ep.cpp -shared -fPIC -o libcustom_ep.so + +test.out:test_pure_virtual.cpp libort.so + g++ -g test_pure_virtual.cpp -L. -lort -o test.out + +// graph_viewer_ref.h. This is the API provided by libort.so +#pragma once +#include + +class GraphViewerRef { +public: + virtual bool IsConstantInitializer(std::string& name, bool check_outer_scope) = 0; +}; + +// graph_view_api_impl.h +#pragma once +#include "graph_viewer_ref.h" + +class ApiGraphView : public GraphViewerRef { + bool IsConstantInitializer(std::string& name, bool check_outer_scope) override; +}; + +// graph_view_api_impl.cpp +#include "graph_view_api_impl.h" + +bool ApiGraphView::IsConstantInitializer(std::string& name, bool check_outer_scope) { + if (check_outer_scope && name == "name") return true; + return false; +} + +// session.h is the same as Solution 1's + +// session.cpp +#include +#include +#include "session.h" +#include "provider.h" +#include "graph_view_api_impl.h" + +class EPAdapter { +public: + EPAdapter(ExecutionProvider* ep) : ep_(ep) {} + int GetCapability(GraphViewerRef* graph) { return ep_->GetCapability(graph); } +private: + ExecutionProvider* ep_; +}; + +void session::RegisterCustomEP(std::string path) { + void* handle = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL); + ExecutionProvider* (*GetExternalProvider)(); + *(void **) (&GetExternalProvider) = dlsym(handle, "GetExternalProvider"); + ep_adapter_ = new EPAdapter((*GetExternalProvider)()); +} + +void session::Run() { + std::cout << "session::Run()\n"; + ApiGraphView* api_graph_view = new ApiGraphView(); + std::cout << "ep graph get capabiliyty: "<GetCapability(api_graph_view)<<"\n"; +} + +session::~session() { + delete ep_adapter_; +} + +// provider.h is the same as Solution 1's +#pragma once +#include "graph_viewer_ref.h" + +class ExecutionProvider { +public: + ExecutionProvider() {}; + virtual ~ExecutionProvider() = default; + + virtual int GetCapability(GraphViewerRef*) { return 0; } +}; + +// custom_ep.h is the same as Solution 1's +#pragma once +#include "provider.h" + +class custom_ep : public ExecutionProvider { +public: + custom_ep(std::string& n); + ~custom_ep() = default; + int GetCapability(GraphViewerRef*) override; +private: + std::string name; +}; + +// custom_ep.cpp is the same as Solution 1's +#include "custom_ep.h" + +custom_ep::custom_ep(std::string& n) { name = n; } + +int custom_ep::GetCapability(GraphViewerRef* graph) { + if (graph->IsConstantInitializer(name, true)) return 1; + return 2; +} + +#ifdef __cplusplus +extern "C" { +#endif + +custom_ep* GetExternalProvider() { + std::string name = "custom_ep"; + return new custom_ep(name); +} + +#ifdef __cplusplus +} +#endif + +// test_pure_virtual.cpp is the same as Solution 1's test_wrapper.cpp +#include "session.h" + +int main() { + session sess; + sess.RegisterCustomEP("/home/leca/code/plugin_pure_virtual/libcustom_ep.so"); + sess.Run(); + return 0; +} +``` + +In this way, there is no such symbol of the function GraphViewerRef::IsConstantInitializer() in custom\_ep.so: + +```bash +leca@host [ ~/code/plugin_pure_virtual ]$ readelf -s --wide libcustom_ep.so | grep -i 'IsConstantInitializer' +leca@host [ ~/code/plugin_pure_virtual ]$ +```