Skip to content

Commit

Permalink
update 2023_10_20_c++CommonErrorsAndSolutions
Browse files Browse the repository at this point in the history
  • Loading branch information
jslhcl committed Aug 7, 2024
1 parent 137bd1e commit 3b31788
Showing 1 changed file with 312 additions and 0 deletions.
312 changes: 312 additions & 0 deletions _posts/2023-10-20-C++-Common-Errors-And-Solutions.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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 <string>

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 <string>

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 <string>
class EPAdapter;

class session {
public:
session() {}
~session();
void RegisterCustomEP(std::string path);
void Run();
private:
EPAdapter* ep_adapter_;
};

// session.cpp
#include <iostream>
#include <dlfcn.h>
#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: "<<ep_adapter_->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 <string>

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 <iostream>
#include <dlfcn.h>
#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: "<<ep_adapter_->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 ]$
```

0 comments on commit 3b31788

Please sign in to comment.