Skip to content

Instantly share code, notes, and snippets.

@carlopi
Created June 30, 2020 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carlopi/73825f9915dfcd00847f37d93eded97c to your computer and use it in GitHub Desktop.
Save carlopi/73825f9915dfcd00847f37d93eded97c to your computer and use it in GitHub Desktop.
Wrapper via pimpl_with_deleter of arbitrary Wasm classes
/////////////////////////////////////////////////////////////////////////////////
//---------------------- BOILERPLATE TO PUT IN A SEPARATE HEADER -----///
/////////////////////////////////////////////////////////////////////////////////
#include <memory>
class [[cheerp::genericjs]][[cheerp::jsexport]] CounterAlive {
public:
CounterAlive()
{
++counter;
}
// ~CounterAlive()
void deleter() //current limitation on the destructor
{
--counter;
}
[[cheerp::jsexport]]
static int numberAlive()
{
return counter;
}
[[cheerp::jsexport]]
void placeholder()
{
}
private:
static int counter;
};
int CounterAlive::counter = 0;
namespace cheerp_pimpl
{
template<typename T>
class [[cheerp::genericjs]] pimpl_with_deleter {
private:
T* m;
CounterAlive counter;
public:
pimpl_with_deleter() : m(new T()) {}
template<typename ...Args> pimpl_with_deleter( Args&& ... args) : m( new T(args...)) {}
/* current limitations on member of [[cheerp::jsexport]]-ed classes impose deleting the destructor
~pimpl_with_deleter()
{
deleter();
}*/
void deleter()
{
counter.deleter();
//Since for now we have to do without destructor
//we declare a class deleter that is manually called and implement the destructor
delete m;
}
T* operator->()
{
return m;
}
T& operator*()
{
return *m;
}
};
} //end namespace cheerp_pimpl
////////////////////////////////////////////////////////////////
///----------------- END BOILERPLATE -------------///
////////////////////////////////////////////////////////////////
#include <vector>
#include <set>
#include <iostream>
//EXAMPLE OF ARBITRARY WASM CLASS WITH NON-TRIVIAL DESTRUCTOR
class [[cheerp::wasm]] WasmClass
{
//Arbitrary class, have no restrictions of sort, can be used both in arbitrary code AND inside a JSExported wrapper
public:
WasmClass(int X)
{
std::cout << "ImplWasm instantiated with value " << X << "\n";
}
void insert(int N)
{
//Insert N both in the set and the vector
V.push_back(N);
bool f = S.insert(N).second;
std::cout << N << " inserted";
if (!f)
std::cout << " but was already present";
std::cout << "\n";
}
int totalNum() const
{
return V.size();
}
int numDifferentItems() const
{
return S.size();
}
~WasmClass()
{
std::cout << "Destructor called....\n";
}
private:
std::vector<int> V;
std::set<int> S;
};
//JSEXPORTED WRAPPER TO DECLARE
class [[cheerp::jsexport]][[cheerp::genericjs]] GenericjsWrapperWithDeleter
{
cheerp_pimpl::pimpl_with_deleter<WasmClass> m;
public:
GenericjsWrapperWithDeleter(int N) : m(N)
{
std::cout << "GenericjsWrapperWithDeleter constructor\n";
}
void insert(int N)
{
m->insert(N);
}
// ~GenericjsWrapperWithDeleter() //Also here, we can't have the destructor... for now we are forced to call deleter
void deleter()
{
{
m.deleter();
std::cout << "GenericjsWrapperWithDeleter \"destructor\" called\n";
}
};
//USE OF THE WASM CLASS FROM C++ CODE
void someWasmFunction()
{
std::cout << "\nCalling someWasmFunction\n";
WasmClass W(23);
W.insert(45);
W.insert(45);
std::cout << "Done calling someWasmFunction\n\n";
//W destructor called...
}
//USE OF THE JSEXPORTED CLASS FROM JAVASCRIPT CODE
void [[cheerp::genericjs]] someOtherGenerericjsFunction()
{
std::cout << "\nCalling someOtherGenericjsFunction\n";
//__asm__ is a way of directing calling JavaScript arbitrary code, but in principle similar code should be on the user side
__asm__("\
var instance = new GenericjsWrapperWithDeleter(100);\
instance.insert(45);\
instance.insert(12334);\
instance.deleter();\
console.log(CounterAlive.numberAlive());\
");
//IN JS WE NEED TO CALL THE DELETER
std::cout << "Done calling someOtherGenericjsFunction\n\n";
}
int main()
{
someWasmFunction();
someOtherGenerericjsFunction();
assert(CounterAlive::numberAlive() == 0);
// You might be willing to create some objects and leave them alive, but be conscious with memory leaks
return 0;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment