| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | title: C++ + Hermes | 
					
						
							|  |  |  | pagination_prev: demos/bigdata/index | 
					
						
							|  |  |  | pagination_next: solutions/input | 
					
						
							|  |  |  | --- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import current from '/version.js'; | 
					
						
							|  |  |  | import CodeBlock from '@theme/CodeBlock'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Hermes is an embeddable JS engine written in C++. With some light shims, it can | 
					
						
							|  |  |  | run the standalone browser scripts. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The [Standalone scripts](/docs/getting-started/installation/standalone) can be | 
					
						
							|  |  |  | parsed and evaluated in a Hermes context. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :::caution Here be Dragons | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The main target for Hermes is React Native.  At the time of writing, there was | 
					
						
							|  |  |  | no official documentation for embedding the Hermes engine in C++ programs. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Integration Details
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _Initialize Hermes_ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The runtime can be initialized in one line: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | std::unique_ptr<facebook::jsi::Runtime> rt(facebook::hermes::makeHermesRuntime()); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Hermes does not expose a `console` or `global` variable, but those can be | 
					
						
							| 
									
										
										
										
											2023-06-20 01:21:34 +00:00
										 |  |  | synthesized from JS code in the runtime. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :::note pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The JavaScript code is shown below: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js | 
					
						
							|  |  |  | /* create global object */ | 
					
						
							|  |  |  | var global = (function(){ return this; }).call(null); | 
					
						
							|  |  |  | /* create a fake `console` from the hermes `print` builtin */ | 
					
						
							|  |  |  | var console = { log: function(x) { print(x); } }; | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The code can be stored in a C string and evaluated after creating a runtime: | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | auto src = std::make_shared<facebook::jsi::StringBuffer>( | 
					
						
							|  |  |  |   /* create global object */ | 
					
						
							|  |  |  |   "var global = (function(){ return this; }).call(null);" | 
					
						
							|  |  |  |   /* create a fake `console` from the hermes `print` builtin */ | 
					
						
							|  |  |  |   "var console = { log: function(x) { print(x); } };" | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | auto js = rt->prepareJavaScript(src, std::string("<eval>")); | 
					
						
							|  |  |  | rt->evaluatePreparedJavaScript(js); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _Load SheetJS Scripts_ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The main library can be loaded by reading the script from the file system and | 
					
						
							|  |  |  | evaluating in the Hermes context: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | static char *read_file(const char *filename, size_t *sz) { | 
					
						
							|  |  |  |   FILE *f = fopen(filename, "rb"); | 
					
						
							|  |  |  |   if(!f) return NULL; | 
					
						
							|  |  |  |   long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); } | 
					
						
							|  |  |  |   char *buf = (char *)malloc(fsize * sizeof(char)); | 
					
						
							|  |  |  |   *sz = fread((void *) buf, 1, fsize, f); | 
					
						
							|  |  |  |   fclose(f); | 
					
						
							|  |  |  |   return buf; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Unfortunately the library provides no C-friendly Buffer classes */ | 
					
						
							|  |  |  | class CBuffer : public facebook::jsi::Buffer { | 
					
						
							|  |  |  |   public: | 
					
						
							|  |  |  |     CBuffer(const uint8_t *data, size_t size) : buf(data), sz(size) {} | 
					
						
							|  |  |  |     size_t size() const override { return sz; } | 
					
						
							|  |  |  |     const uint8_t *data() const override { return buf; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private: | 
					
						
							|  |  |  |     const uint8_t *buf; | 
					
						
							|  |  |  |     size_t sz; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ... | 
					
						
							|  |  |  |   /* load SheetJS library */ | 
					
						
							|  |  |  |   size_t sz; char *xlsx_full_min_js = read_file("xlsx.full.min.js", &sz); | 
					
						
							|  |  |  |   auto src = std::make_shared<CBuffer>(CBuffer((uint8_t *)xlsx_full_min_js, sz)); | 
					
						
							|  |  |  |   auto js = rt->prepareJavaScript(src, std::string("xlsx.full.min.js")); | 
					
						
							|  |  |  |   rt->evaluatePreparedJavaScript(js); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | To confirm the library is loaded, `XLSX.version` can be printed to the console: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  | auto src = std::make_shared<facebook::jsi::StringBuffer>( | 
					
						
							|  |  |  |   "console.log('SheetJS Library Version: ' + XLSX.version)" | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | auto js = rt->prepareJavaScript(src, std::string("<eval>")); | 
					
						
							|  |  |  | rt->evaluatePreparedJavaScript(js); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Reading Files
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Hermes supports `ArrayBuffer` but has no simple helper to read raw memory. | 
					
						
							|  |  |  | Libraries are expected to implement `MutableBuffer`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							| 
									
										
										
										
											2023-06-20 01:21:34 +00:00
										 |  |  | /* ArrayBuffer constructor expects MutableBuffer */ | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | class CMutableBuffer : public facebook::jsi::MutableBuffer { | 
					
						
							|  |  |  |   public: | 
					
						
							|  |  |  |     CMutableBuffer(uint8_t *data, size_t size) : buf(data), sz(size) {} | 
					
						
							|  |  |  |     size_t size() const override { return sz; } | 
					
						
							|  |  |  |     uint8_t *data() override { return buf; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private: | 
					
						
							|  |  |  |     uint8_t *buf; | 
					
						
							|  |  |  |     size_t sz; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | // ... | 
					
						
							|  |  |  |   /* load payload as ArrayBuffer */ | 
					
						
							|  |  |  |   size_t sz; char *data = read_file(argv[1], &sz); | 
					
						
							|  |  |  |   auto payload = std::make_shared<CMutableBuffer>(CMutableBuffer((uint8_t *)data, sz)); | 
					
						
							|  |  |  |   auto ab = facebook::jsi::ArrayBuffer(*rt, payload); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | It is strongly recommended to create a stub function to perform the entire | 
					
						
							|  |  |  | workflow in JS code and pass the final result back to C++. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 01:21:34 +00:00
										 |  |  | :::note pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The stub function will be passed an `ArrayBuffer` object: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | ```js | 
					
						
							|  |  |  | function(buf) { | 
					
						
							|  |  |  |   /* `buf` will be an ArrayBuffer */ | 
					
						
							|  |  |  |   var wb = XLSX.read(buf); | 
					
						
							|  |  |  |   return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-20 01:21:34 +00:00
										 |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | _C++ integration code_ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```cpp | 
					
						
							|  |  |  |   /* define stub function to read and convert first sheet to CSV */ | 
					
						
							|  |  |  |   auto src = std::make_shared<facebook::jsi::StringBuffer>( | 
					
						
							|  |  |  |     "(function(buf) {" | 
					
						
							|  |  |  |       "var wb = XLSX.read(buf);" | 
					
						
							|  |  |  |       "return XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);" | 
					
						
							|  |  |  |     "})" | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   auto js = rt->prepareJavaScript(src, std::string("<eval>")); | 
					
						
							|  |  |  |   auto func = rt->evaluatePreparedJavaScript(js); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* call stub function and capture result */ | 
					
						
							|  |  |  |   auto csv = func.asObject(*rt).asFunction(*rt).call(*rt, ab); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* interpret as utf8 and print to stdout */ | 
					
						
							|  |  |  |   std::string str = csv.getString(*rt).utf8(*rt); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Complete Example
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The "Integration Example" covers a traditional integration in a C++ application, | 
					
						
							|  |  |  | while the "CLI Test" demonstrates other concepts using the `hermes` CLI tool. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Integration Example
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :::note | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | This demo was tested in the following deployments: | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | | Architecture | Git Commit | Date       | | 
					
						
							|  |  |  | |:-------------|:-----------|:-----------| | 
					
						
							|  |  |  | | `darwin-x64` | `869312f`  | 2023-05-30 | | 
					
						
							|  |  |  | | `darwin-arm` | `869312f`  | 2023-06-05 | | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | 0) Install build dependencies: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | brew install icu4c cmake ninja | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 1) Make a project directory: | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | mkdir sheetjs-hermes | 
					
						
							|  |  |  | cd sheetjs-hermes | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | 2) Download the [`Makefile`](pathname:///hermes/Makefile): | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | curl -LO https://docs.sheetjs.com/hermes/Makefile | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | 3) Download [`sheetjs-hermes.cpp`](pathname:///hermes/sheetjs-hermes.cpp): | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | curl -LO https://docs.sheetjs.com/hermes/sheetjs-hermes.cpp | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | 4) Build the library (this is the `init` target): | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | make init | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | 5) Build the application: | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | make sheetjs-hermes | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | 6) Download the standalone script and test file: | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | <ul> | 
					
						
							|  |  |  | <li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li> | 
					
						
							|  |  |  | <li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li> | 
					
						
							|  |  |  | </ul> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <CodeBlock language="bash">{`\ | 
					
						
							|  |  |  | curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js | 
					
						
							|  |  |  | curl -LO https://sheetjs.com/pres.numbers`} | 
					
						
							|  |  |  | </CodeBlock> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-05 20:12:53 +00:00
										 |  |  | 7) Run the application: | 
					
						
							| 
									
										
										
										
											2023-05-30 06:41:09 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | ./sheetjs-hermes pres.numbers | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | If successful, the program will print the library version number and the | 
					
						
							|  |  |  | contents of the first sheet as CSV rows. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### CLI Test
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | :::note | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This demo was last tested on 2023 May 30 against Hermes version `0.11.0`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ::: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Due to limitations of the standalone binary, this demo will encode a test file | 
					
						
							|  |  |  | as a Base64 string and directly add it to an amalgamated script. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 0) Install the `hermes` command line tool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 1) Download the standalone script and test file: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <ul> | 
					
						
							|  |  |  | <li><a href={`https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li> | 
					
						
							|  |  |  | <li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li> | 
					
						
							|  |  |  | </ul> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <CodeBlock language="bash">{`\ | 
					
						
							|  |  |  | curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js | 
					
						
							|  |  |  | curl -LO https://sheetjs.com/pres.numbers`} | 
					
						
							|  |  |  | </CodeBlock> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 2) Bundle the test file and create `payload.js`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | node -e "fs.writeFileSync('payload.js', 'var payload = \"' + fs.readFileSync('pres.numbers').toString('base64') + '\";')" | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 3) Create support scripts: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - `global.js` creates a `global` variable and defines a fake `console`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js title="global.js" | 
					
						
							|  |  |  | var global = (function(){ return this; }).call(null); | 
					
						
							|  |  |  | var console = { log: function(x) { print(x); } }; | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - `hermes.js` will call `XLSX.read` and `XLSX.utils.sheet_to_csv`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```js title="hermes.js" | 
					
						
							|  |  |  | var wb = XLSX.read(payload, {type:'base64'}); | 
					
						
							|  |  |  | console.log(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]])); | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 4) Create the amalgamation `xlsx.hermes.js`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | cat global.js xlsx.full.min.js payload.js hermes.js > xlsx.hermes.js | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The final script defines `global` before loading the standalone library.  Once | 
					
						
							|  |  |  | ready, it will read the bundled test data and print the contents as CSV. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 5) Run the script using the Hermes standalone binary: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```bash | 
					
						
							|  |  |  | hermes xlsx.hermes.js | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | If successful, the script will print CSV data from the test file |