2020-05-03 13:17:35 +00:00
|
|
|
# Wrenpp #
|
|
|
|
|
|
|
|
Wrenpp is a lightweight C++ wrapper for the official [Wren](http://wren.io/) C
|
|
|
|
library. It aims to package all the features accessible through the C API in a
|
|
|
|
modern C++ interface that is easy to use, while keeping the overhead to a
|
|
|
|
minimum.
|
|
|
|
|
2022-05-15 21:09:36 +00:00
|
|
|
* [Current state](#current-state)
|
|
|
|
* [Philosophy](#philosophy)
|
2020-05-03 13:17:35 +00:00
|
|
|
* [Building](#building)
|
|
|
|
* [Wrenpp](#wrenpp)
|
|
|
|
* [Wren only](#wren-only)
|
|
|
|
* [Usage](#usage)
|
|
|
|
* [Meson](#meson)
|
|
|
|
* [C++](#c)
|
|
|
|
* [Header files](#header-files)
|
|
|
|
* [Initialisation](#initialisation)
|
2022-05-15 21:09:36 +00:00
|
|
|
* [Foreign functions](#foreign-functions)
|
|
|
|
* [Implementation](#implementation)
|
|
|
|
|
|
|
|
## Current state ##
|
|
|
|
|
|
|
|
The library is functional and provides access to most of the features of Wren.
|
|
|
|
Some are better tested than others. There are no known memory leaks. Please
|
|
|
|
keep in mind this is an early version and more testing and work will be needed.
|
|
|
|
|
|
|
|
## Philosophy ##
|
|
|
|
|
|
|
|
The overall principle behind this wrapper is to be feature rich while also
|
|
|
|
being lightweight and allowing fine-grained control for users who need it.
|
|
|
|
|
|
|
|
By design, wrenpp hides the Wren C API entirely, but all functions (considered
|
|
|
|
"low level" relative to this wrapper) are still accessible through the `VM`
|
|
|
|
class. If the function you need is not available there it's probably just
|
|
|
|
missing and adding it should be a trivial task.
|
|
|
|
|
|
|
|
Wrenpp provides you with convenience functions in `vm_fun.hpp` that are built
|
|
|
|
on top of the `VM` class and make basic use of Wren simpler. You're still free
|
|
|
|
to "mix & match" higher level functions with lower level ones, and you can
|
|
|
|
decide to not use portions of the wrapper entirely.
|
2020-05-03 13:17:35 +00:00
|
|
|
|
|
|
|
## Building ##
|
|
|
|
|
|
|
|
### Wrenpp ###
|
|
|
|
|
|
|
|
You can make a release build like this:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
mkdir build
|
|
|
|
cd build
|
|
|
|
meson setup -Dbuildtype=release ..
|
|
|
|
ninja
|
|
|
|
```
|
|
|
|
|
|
|
|
The build scripts understand the following options (double check in
|
|
|
|
`meson_options.txt`):
|
|
|
|
|
|
|
|
1. `build_testing` set to true to compile Wren's unit tests too
|
|
|
|
2. `build_examples` set to true to include examples from the `examples/`
|
|
|
|
directory in the build
|
|
|
|
3. `wren_with_cli` set to false to exclude the command line tool from the Wren
|
|
|
|
subproject build
|
|
|
|
4. `wren_with_rand` set to true to compile Wren with
|
|
|
|
[random](http://wren.io/modules/random/) support
|
|
|
|
5. `wren_with_meta` set to true to compile Wren with
|
|
|
|
[meta](http://wren.io/modules/meta/) support
|
2022-05-15 21:09:36 +00:00
|
|
|
6. `wrenpp_with_name_guessing` enable class name guessing in c++ code. It
|
|
|
|
currently affects only example code and header files, not the library
|
|
|
|
itself. It controls the `WRENPP_WITH_NAME_GUESSING` preprocessor symbol.
|
|
|
|
When it's defined, you will have access to better error reporting and extra
|
|
|
|
functions, such as a `make_wren_object()` overload that doesn't require you
|
|
|
|
to specify a string class name.
|
2020-05-03 13:17:35 +00:00
|
|
|
|
|
|
|
Note that some example projects require random support in Wren, without which
|
|
|
|
they will crash. If you want to run examples or you are getting "Address
|
|
|
|
boundary error" segfaults recompile Wrenpp after enabling the example and/or
|
|
|
|
meta options:
|
|
|
|
|
|
|
|
```
|
|
|
|
mkdir build
|
|
|
|
cd build
|
|
|
|
meson setup -Dbuildtype=debug -Dbuild_examples=true -Dwren_with_rand=true -Dwren_with_meta=true ..
|
|
|
|
ninja
|
|
|
|
```
|
|
|
|
|
|
|
|
### Wren only ###
|
|
|
|
|
|
|
|
If you only want to compile the C library of Wren, of course you can do that:
|
|
|
|
|
|
|
|
```
|
|
|
|
mkdir build
|
|
|
|
cd build
|
|
|
|
meson setup -Dbuildtype=release -Dwren_with_rand=true -Dwren_with_meta=true ../subprojects/wren
|
|
|
|
ninja
|
|
|
|
```
|
|
|
|
|
|
|
|
This will build Wren as a shared object. If you want a static library instead
|
|
|
|
you can add the `-Ddefault_library=static` parameter to your Meson command, or
|
|
|
|
`-Ddefault_library=both` if you want tboth the shared and the static version of
|
|
|
|
Wren.
|
|
|
|
|
|
|
|
## Usage ##
|
|
|
|
|
|
|
|
### Meson ###
|
|
|
|
|
|
|
|
You can use Wrenpp as a Meson
|
|
|
|
[subproject](https://mesonbuild.com/Reference-manual.html#subproject) like you
|
|
|
|
would do normally. For example:
|
|
|
|
|
|
|
|
```
|
|
|
|
wrenpp = subproject('wrenpp', default_options: ['wren_with_rand=true'])
|
|
|
|
wrenpp_dep = wrenpp.get_variable('wrenpp_dep')
|
|
|
|
|
|
|
|
executable('my_project',
|
|
|
|
'main.cpp',
|
|
|
|
dependencies: wrenpp_dep,
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
This will make Wrenpp build as part of your project with Wren's optional random
|
|
|
|
module enabled.
|
|
|
|
|
|
|
|
Alternatively you can use the subproject as a fallback if there is no Wrenpp
|
|
|
|
installed in your system already:
|
|
|
|
|
|
|
|
```
|
|
|
|
wrenpp_dep = dependency('wrenpp', version: '>=0.1.0',
|
|
|
|
fallback: ['wrenpp', 'wrenpp_dep'],
|
|
|
|
default_options: ['wren_with_rand=true'],
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that when you use Wrenpp as a subproject, Wrenpp's subproject Wren will
|
|
|
|
become a sub-subproject of your project. This is how Meson works and it simply
|
|
|
|
means that in your top level source directory you will have to run this command
|
|
|
|
before you will be able to compile (Meson should detect and print this as
|
|
|
|
well):
|
|
|
|
|
|
|
|
```
|
|
|
|
meson wrap promote subprojects/wrenpp/subprojects/wren
|
|
|
|
```
|
|
|
|
|
|
|
|
### C++ ###
|
|
|
|
|
|
|
|
For working examples refer to the source files in the `examples/` directory.
|
2022-05-15 21:09:36 +00:00
|
|
|
"Greet" is the simplest example, while "math_vector" is showing off more
|
|
|
|
features.
|
2020-05-03 13:17:35 +00:00
|
|
|
|
|
|
|
#### Header files ####
|
|
|
|
|
|
|
|
In order to use Wrenpp in your project you will need to include `wrenpp/vm.hpp`
|
|
|
|
at least. A description of all the relevant header files follows:
|
|
|
|
|
|
|
|
* `vm.hpp` The `VM` class provides simple wrappers around the C API functions.
|
|
|
|
It wraps and hides the `WrenVM` pointer and provides RAII around it.
|
|
|
|
Available methods aim to be as close to their C counterparts as possible. You
|
|
|
|
can use objects of this class in a way that mirrors very closely the way you
|
|
|
|
would use the C API.
|
|
|
|
* `vm_fun.hpp` This header provides functions on top of the `VM` class. Their
|
|
|
|
goal is to make common operations easier and more compact in your code.
|
|
|
|
* `def_configuration.hpp` The `DefConfiguration` struct extends the bare
|
|
|
|
`Configuration` struct by providing an implementation for `write_fn`,
|
|
|
|
`error_fn` and `reallocate_fn`. They are implemented in terms of `std::cout`,
|
|
|
|
`std::cerr` and `new[]`/`delete[]` respectively. You can safely ignore this
|
|
|
|
header if this is not suitable for your project.
|
|
|
|
|
|
|
|
Other header files are intended for Wrenpp's use and you shouldn't need to use
|
|
|
|
them. You are free to use them in your project if you think the code inside is
|
|
|
|
useful to you, however keep in mind that those are not providing core Wrenpp
|
|
|
|
functionalities and may be removed or modified in the future.
|
|
|
|
|
|
|
|
|
|
|
|
#### Initialisation ####
|
|
|
|
|
|
|
|
Wrenpp aims to make your code as clear and concise as possible. To instantiate
|
|
|
|
a `VM` (which wraps `WrenVM`) and run a simple script you can do the following:
|
|
|
|
|
|
|
|
```
|
|
|
|
#include <wrenpp/vm.hpp>
|
|
|
|
#include <wrenpp/def_configuration.hpp>
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
wren::DefConfiguration config;
|
|
|
|
wren::VM vm(&config, nullptr);
|
|
|
|
|
|
|
|
vm.interpret("main", "System.print(\"Hello world!\")");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2022-05-15 21:09:36 +00:00
|
|
|
#### Foreign functions ####
|
|
|
|
|
|
|
|
You have two options to register foreign functions with wrenpp: the easiest one
|
|
|
|
is to just use the callback manager available through the `VM` object:
|
|
|
|
|
|
|
|
```
|
|
|
|
#include <wrenpp/vm.hpp>
|
|
|
|
#include <wrenpp/def_configuration.hpp>
|
|
|
|
#include <wrenpp/callback_manager.hpp> //necessary if you want to use the callback manager
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
wren::DefConfiguration config;
|
|
|
|
wren::VM vm(&config, nullptr);
|
|
|
|
|
|
|
|
vm.callback_manager()
|
|
|
|
.add_callback(true, "your_module", "YourClass", "your_method(_,_)", &your_method);
|
|
|
|
|
|
|
|
vm.interpret("main", your_fun_script_here);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The current implementation available in `DefConfiguration` registers a callback
|
|
|
|
to Wren that looks up a `std::unordered_map` for the correct c++ function and
|
|
|
|
then forwards the call to it. This should be good enough for most cases,
|
|
|
|
however you are free to provide your own callback for wren by writing your own
|
|
|
|
Configuration class. Wrenpp tries to not impose you any particular helper code,
|
|
|
|
in fact if you don't wish to use the `CallbackManager` returned by
|
|
|
|
`vm.callback_manager()` then you don't even need to include the relative header
|
|
|
|
file! There will still be a `std::unordered_map` in memory (because that's
|
|
|
|
owned by `vm`) but it will never be used.
|
|
|
|
|
|
|
|
The second method requires you to provide the full Wren foreign functions
|
|
|
|
callback:
|
|
|
|
|
|
|
|
```
|
|
|
|
#include <wrenpp/vm.hpp>
|
|
|
|
#include <wrenpp/def_configuration.hpp>
|
|
|
|
|
|
|
|
class MyConf : public wren::DefConfiguration {
|
|
|
|
public:
|
|
|
|
//This is the callback that Wren will call, refer to the C API documentation
|
|
|
|
//for more details.
|
|
|
|
wren::foreign_method_t foreign_method_fn(
|
|
|
|
wren::VM* vm,
|
|
|
|
std::string_view module,
|
|
|
|
std::string_view class_name,
|
|
|
|
bool is_static,
|
|
|
|
std::string_view signature
|
|
|
|
) {
|
|
|
|
if (module == "your_module" and class_name == "YourClass") {
|
|
|
|
if (is_static and signature == "your_method(_,_)")
|
|
|
|
return &your_method;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
MyConf config;
|
|
|
|
wren::VM vm(&config, nullptr);
|
|
|
|
|
|
|
|
vm.interpret("main", your_fun_script_here);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The above code is very close to what you'd do with the C API. If you need, you
|
|
|
|
can even pass a user data pointer to the `VM` constructor (`nullptr` in this
|
|
|
|
snippet) and retrieve it later from within `foreign_method_fn()` via
|
|
|
|
`vm.void_user_data()` or `vm.user_data()`. You're unlikely to need it as, as
|
|
|
|
you can see, `foreign_method_fn()` is not static and so you can have some state
|
|
|
|
stored in your `MyConf` object, however you still get the chance to use the
|
|
|
|
C-like user data mechanism if you wish to.
|
|
|
|
|
|
|
|
Note that for the above to work the `foreign_method_fn()` method in `MyConf`
|
|
|
|
must be named exactly like that, and the signature must match (except for the
|
|
|
|
`static` and `const` keywords). Ideally the code above should alse be
|
|
|
|
`noexcept` as there will be C code in the call stack when that method is
|
|
|
|
invoked.
|
|
|
|
|
|
|
|
## Implementation ##
|
|
|
|
|
2020-05-03 13:17:35 +00:00
|
|
|
*Work in progress*
|