Coder Social home page Coder Social logo

matusnovak / wrenbind17 Goto Github PK

View Code? Open in Web Editor NEW
62.0 5.0 9.0 10.65 MB

A header only library for binding C++17 classes and functions to Wren, an embeddable programming language

Home Page: https://matusnovak.github.io/wrenbind17

License: MIT License

CMake 1.24% C++ 98.67% Shell 0.09%
wren wren-language wren-helpers cpp17 embedded-language scripting scripting-language binding-generator

wrenbind17's Introduction

WrenBind17

build release codecov

WrenBind17 is a C++17 wrapper for Wren programming language. This project was heavily inspired by pybind11 and by Wren++. This library is header only and does not need any compilation steps. Simply include the WrenBind17 header <wrenbind17/wrenbind17.hpp>, link the Wren library, and you are good to go.

https://matusnovak.github.io/wrenbind17

Features

  • Header only.
  • Works with Visual Studio 2017/2019 (x64 or x86), MinGW-w64 (x64 or x86), Linux GCC (x64 or arm64), and Apple Clang on Mac OSX (x64).
  • C++17 so you don't need to use decltype() on class methods to bind them to Wren.
  • Foreign modules are automatically generated for you. You don't need to write the extra foreign classes in separate file.
  • Supports strict type safety. You won't be able to pass just any variable from Wren back to the C++, preventing you getting segmentation faults.
  • Objects are wrapped in std::shared_ptr so you have easier access when passing objects around. This also enables easy object lifetime management.
  • Easy binding system inspired by pybind11.
  • Works with exceptions.
  • Upcasting to base types when passing C++ instances.
  • Memory leak tested.
  • Supports STL containers such as std::variant, std::optional, std::vector, std::list, std::deque, std::set, std::unordered_set, std::map, std::unordered_map which can be handled either natively or as a foreign class.
  • Easy binding of operators such as +, -, [], etc.
  • Long but easy to follow tutorial (here).
  • Supports native lists and native maps.
  • Supports Fn.new{}.
  • Supports inheritance (a workaround).
  • Supports modularity via look-up paths.
  • Supports passing variables by move.

Limitations

  • Passing by a reference to a Wren function will create a copy. Use a pointer if you do not wish to create copies and maintain single instance of a given class. This does not affect C++ member functions that return a reference, in that case it will be treated exactly same as a pointer.
  • STL containers std::unique_ptr, std::queue, std::stack are not supported.

TODO

  • Lambdas
  • .., ..., and is operators
  • lcov coverage (currently broken with gcc-9)

Example

#include <wrenbind17/wrenbind17.hpp>
namespace wren = wrenbind17; // Alias

class MyFoo {
public:
    MyFoo(int bar, const std::string& baz);
    
    const std::string& getMessage() const;

    // Properties
    int getYear() const;
    void setYear(int value);

    // Variables
    std::string message;
};

int main(int argc, char *argv[]) {
    const std::string code = R"(
        import "mymodule" for MyFoo, Vec3

        class Main {
            static main() {
                var vec = Vec3.new(1.1, 2.2, 3.3)
                // Do something with "vec"

                var foo = MyFoo.new(2019, "Hello World")
                System.print("Foo type: %(foo.type) ")
                foo.year = 2020
                return foo
            }
        }
    )";

    // Create new VM
    wren::VM vm;
    
    // Create new module
    auto& m = vm.module("mymodule");

    // Add new class
    auto& cls = m.klass<MyFoo>("MyFoo");

    // Add optional constructor
    cls.ctor<int, const std::string&>();

    // Add functions
    cls.func<&MyFoo::getMessage>("getMessage");

    // Define variables
    cls.var<&MyFoo::message>("message"); // Direct access
    cls.prop<&MyFoo::getYear, &MyFoo::setYear>("year"); // As getter & getter
    cls.propReadonly<&MyFoo::getType>("type"); // Read only variable

    // Append some extra stuff to the "mymodule"
    m.append(R"(
        class Vec3 {
            construct new (x, y, z) {
                _x = x
                _y = y
                _z = z
            }
        }
    )");

    // Runs the code from the std::string as a "main" module
    vm.runFromSource("main", code);

    // Find the main() function
    // You can look for classes and their functions!
    auto mainClass = vm.find("main", "Main");
    auto main = mainClass.func("main()");

    auto res = main(); // Execute the function

    // Access the return value
    MyFoo* ptr = res.as<MyFoo*>(); // As a raw pointer
    std::shared_ptr<MyFoo> sptr = res.shared<MyFoo>(); // As a shared ptr

    assert(ptr);
    assert(ptr == sptr.get());

    return 0;
}

Documentation

Tutorials and API documentation can be found here: https://matusnovak.github.io/wrenbind17/

Build

There is no need to build this library, it's header only. Simply add the #include <wrenbind17/wrenbind17.hpp> and link Wren to your application. That's all.

Found a bug or want to request a feature?

Feel free to do it on GitHub issues

Pull requests

Pull requests are welcome

License

MIT License

Copyright (c) 2019-2022 Matus Novak

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

wrenbind17's People

Contributors

derthorsten avatar matusnovak avatar overshifted avatar possiblyashrub avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

wrenbind17's Issues

CMake: Allow to skip install

There should be an option like WRENBIND17_SKIP_INSTALL - when using CMake libraries as a submodule it's not always favorable to have install to be run

Add mode without exceptions

Hello,
thanks for that excellent library!
Would it be possible that functions like runFromSource and runFromFile return an error code instead of throwing exceptions?

Binding multiple constructors from C++ class

Is it possible to bind multiple constructors with this library? I've tried doing it with the following sample code without success.

class test
{
    public:
        test(const std::string& arg1)
            { std::cout << std::format("{}\n", arg1); }
        test(const std::string& arg1, const std::string& arg2)
            { std::cout << std::format("{}, {}\n", arg1, arg2); }
};

int main(int argc, char* argv[])
{
    auto vm = wren::VM();
    auto& example = vm.module("example");
    auto& lib = example.klass<test>("test");

    lib.ctor<const std::string&>();
    lib.ctor<const std::string&, const std::string&>();

    try
    {
        vm.runFromSource("main", R"(
            import "example" for test

            var test1 = test.new("1")
            var test2 = test.new("1", "2")
        )");
    }
    catch (const std::exception& e)
        { std::cerr << e.what() << std::endl; }

    return 0;
}
Runtime error: test metaclass does not implement 'new(_)'.
  at: main:4

Passing a map to C++

Hello,

I am playing around with wrenbind17 and I can not figure out how to pass a Wren map or Wren array from Wren to C++.
Is that possible?

Thanks,
David

Derived class returned as a base class from C++ cannot use derived methods

Hello.

I have been using ChaiScript and I am migrating to Wren because of the fibers stuff that I need badly :)

First, thanks a lot for this project, it is making things much easier so far.
I would like to point out one thing that worked in ChaiScript but does not in wrenbind17 that is related to inheritance.

I have registered the base class with its methods and the derived class with all methods, derived and base.
I am using module.klass<derived, base>("derived") and registering the funcs I want.

So, let us say I have derived::derivedMethod in C++. The following will not work

// C++ code
shared_ptr<BaseClass> BaseClass::factory(std::string_view className) {
          return some_derived_class_from_base_class;
}

//wren code
_myInstance =  BaseClass.factory("derived")
_myInstance.derivedMethod()

Factory is returning a base class shared pointer but the real instance held is of type derived. In ChaiScript the derived methods will work, but in WrenBind17 the following error is shown:

exception: Runtime error: base does not implement 'derivedMethod()'

Any workaround? Could this be implemented in some way? Maybe I could help, but not sure how much time I will have available. Depends on difficulty. Thanks!

Base class construction in Wren script leaks destructor calls from derived class

Hello everyone.

I found an issue which seems to be a bug, not sure if it is wrenbind-specific or it is just wren.

The bug is the following. Given this:

// C++
class Base {
 public:
   virtual ~Base();

   static std::shared_ptr<Base> factory();
};

class Derived {
public:
   virtual ~Derived() { ... }
};

std::shared_ptr<Base> Base::factory() { return std::make_shared<Derived>(); }

// Wren code
_myInstanceVar = Base.factory();

Since _myInstanceVar holds a Derived but returns a Base, it seems that Wren does not call the destructor for Derived at all.

This seems to be unexpected I think and it was causing a crash in my own code.

calling method with std::list throws assertion error

I have the following Wren code in "main.wren"

class MyClass {
    construct new() { }

    flyTo(city) {
        System.print("Flying to %(city)")
    }

    flyToList(cities) {
        System.print("flyToList.count %(cities.count)")

        cities.each { |word|
            System.print("Flying to %(word) FF")
        }
    }
}

var t = MyClass.new()

And I have the following C++ code:

int main()
{
    const char *module = "main";
    std::string src = read_main("src/main.wren");

    wren::VM vm;

    vm.runFromSource(module, src);

    std::list<int> ml;
    ml.push_back(2);

    wren::Method listCall = vm.find(module, "t").func("flyToList(_)");
    listCall(ml);

    return 0;
}

Running the C++ code throws the following error:

[../wren/src/vm/wren_vm.c:1658] Assert failed in validateApiSlot(): Not that many slots.

If I remove the ml.push_back(2); line, than everything is okay:

flyToList.count 0

What am I getting wrong?
Thank you.

random

Optional modules (meta and random) are not available

import "random" for Random

Wren foreign method seed_() not found in C++

Unable to assign values to binded classes which has binded class members.

I've a got a simpleVector2 struct consisting of x and y.
I've binded it.
Then I've got a more complex struct, Camera2D

typedef struct Camera2D {
    Vector2 offset;         // Camera offset (displacement from target)
    Vector2 target;         // Camera target (rotation and zoom origin)
    float rotation;         // Camera rotation in degrees
    float zoom;             // Camera zoom (scaling), should be 1.0f by default
} Camera2D;

I've also binded that too like

auto& Camera2DCls = RaylibMod.klass<Camera2D>("Camera2D");
Camera2DCls.ctor<>();
Camera2DCls.var<&Camera2D::offset>("offset");
Camera2DCls.var<&Camera2D::target>("target");
Camera2DCls.var<&Camera2D::rotation>("rotation");
Camera2DCls.var<&Camera2D::zoom>("zoom");

And I'm using it like this like following in wren side.

_Cam = Camera2D.new()

_Cam.offset.x = 1280 / 2
_Cam.offset.y = 768 / 2

System.print("Cam Offset: %(_Cam.offset.x), %(_Cam.offset.y)")

But no matter what I do, offest is always 0 for both x and y.

Support std::string_view

Hey,

it's currently not possible to bind a C++ function taking a std::string_view with the expected behaviour.
Could this be added please?

binding operator method

my simple class:

struct Vec2
{
  f32 x, y;
  Vec2() : x(0), y(0) {}
  Vec2(f32 x, f32 y) : x(x), y(y) {}
  Vec2(const Vec2& o) : x(o.x), y(o.y) {}
  Vec2 operator+(const Vec2& a) const { return Vec2(x + a.x, y + a.y); }
  Vec2 operator-(const Vec2& a) const { return Vec2(x - a.x, y - a.y); }
  Vec2 operator*(f32 s) const { return Vec2(x * s, y * s); }
}

auto& m = vm->module("vec2");
auto& cls = m.klass<Vec2>("Vec2");
cls.ctor<f32, f32>();
cls.var<&Vec2::x>("x");
cls.var<&Vec2::y>("y");
cls.func<&Vec2::operator*>("*");

compiler output:

In file included from /Users/el/a/a/scripting.mm:2:
In file included from a/scripting.h:5:
In file included from a/wrenbind17/wrenbind17.hpp:8:
In file included from a/wrenbind17/vm.hpp:9:
In file included from a/wrenbind17/module.hpp:4:
a/wrenbind17/foreign.hpp:392:24: error: implicit instantiation of undefined template 'wrenbind17::ForeignKlassImpl<Vec2>::ForeignMethodDetails<Vec2 (Vec2::*)(float) const, &Vec2::operator*>'
            auto ptr = ForeignMethodDetails<decltype(Fn), Fn>::make(std::move(name));
                       ^
/Users/el/a/a/scripting.mm:733:7: note: in instantiation of function template specialization 'wrenbind17::ForeignKlassImpl<Vec2>::func<&Vec2::operator*>' requested here
  cls.func<&Vec2::operator*>("*");
      ^
In file included from /Users/el/a/a/scripting.mm:2:
In file included from a/scripting.h:5:
In file included from 
a/wrenbind17/wrenbind17.hpp:8:
In file included from a/wrenbind17/vm.hpp:9:
In file included from a/wrenbind17/module.hpp:4:
a/wrenbind17/foreign.hpp:293:16: note: template is declared here
        struct ForeignMethodDetails;
               ^
a/wrenbind17/foreign.hpp:392:62: error: 
incomplete definition of type 'wrenbind17::ForeignKlassImpl<Vec2>::ForeignMethodDetails<Vec2 (Vec2::*)(float) const, &Vec2::operator*>'
            auto ptr = ForeignMethodDetails<decltype(Fn), Fn>::make(std::move(name));
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.