kunitoki / luabridge3 Goto Github PK
View Code? Open in Web Editor NEWA lightweight, dependency-free library for binding Lua to C++
Home Page: https://kunitoki.github.io/LuaBridge3/Manual
License: Other
A lightweight, dependency-free library for binding Lua to C++
Home Page: https://kunitoki.github.io/LuaBridge3/Manual
License: Other
Before 2019 or so, it was possible to get the _class
, _propget
, and _propset
tables for a class and for a namespace. This allowed Lua scripts to reflect classes and instances, which I used for diagnostic and regression testing. (Also for a class browser to facility easier programming.) However, this access was closed down due (apparently) to security concerns. I now build in extra tables that allow for reflection when I register the classes, but this complicates my registration process when I want to add in a new C++ framework that has challenges (such as overloaded functions) that make it difficult or impossible to include in my standard registration process.
I can imagine that in many contexts (I am thinking games) the Lua environment needs to be relatively locked down. But I am working in a non-hostile environment where the user has to opt in to the Lua environment by installing the Lua plugin, and even then there is little motivation to cheat the system. So I am wondering if LuaBridge3 has an option to expose the class tables and if not, would you consider adding it?
c++ code:
LuaRef function_ref=luabridge::getGlobal(lua_state,"compare_game_object");
if(function_ref.isFunction()){
auto game_object=new GameObject();
function_ref(game_object,game_object);
}
lua code:
function compare_game_object(a,b)
print(a,b)
print(a==b)
end
result:
userdata: 000002790b546a28 userdata: 000002790b546a78
false
I want to use it on c11 to surpport std::enable_shared_from_this
// cpp code
// UserdataPtr Can't call
luabridge::LuaRef func = luabridge::getGlobal(L, func_name);
luabridge::LuaResult result = func("helloworld");
// const char* yes
luabridge::LuaRef func = luabridge::getGlobal(L, func_name);
const char* str = "helloworld";
luabridge::LuaResult result = func(name);
// lua code
function test(args)
print(args)
end
I asked this on the LB2 repo, and I will ask it again here. Any plans to support the new Lua 4 <close>
variables?
I've noticed that a number of Lua environments, after sitting on Lua 5.1 or 5.2 for a long time, are finally making the jump to Lua 5.4. (Mine included.) It would be nice to be able to add _close
to some of my classes.
Hi!
I ran into a problem when recently I upgraded my project setup to use LuaJIT + LuaBridge 3.
To isolate it, I wrote a simple test:
#include "lua.hpp"
#include "LuaBridge.h"
class Test
{
public:
static int sum(int x, int y)
{
return x + y;
}
};
int main()
{
auto L = luaL_newstate();
luaL_openlibs(L);
luabridge::getGlobalNamespace(L)
.beginClass<Test>("Test")
.addStaticFunction("sum", &Test::sum)
.endClass();
}
And then I get the error stated in the title. It occurs as soon as beginClass()
gets called.
Might be important to note here, that it doesn't occur with regular Lua and also it doesn't occur with LuaJIT + LuaBridge 2.8.
Also might be important that any other stuff I do works great, i.e. I can do .beginNamespace()
, addFunction()
etc. no problem. The error caused ONLY by beginClass()
according to what I have tested so far.
LuaJIT version I used is 2.1.0-beta3, and I also noticed LuaBridge 3 tests running on that same LuaJIT version pass just fine, so I added this test code as a separate test in ClassTests.cpp
and it worked correctly. So I suppose I'm not doing something important in Lua initialization, or maybe it's some #define
's I need, I'm not sure.
The following function in Expected.h is marked as noexcept
but throws if LUABRIDGE_HAS_EXCEPTIONS
is true.
constexpr const T&& value() const&& noexcept
{
#if LUABRIDGE_HAS_EXCEPTIONS
if (!hasValue())
throw BadExpectedAccess<E>(error());
#endif
return std::move(base_type::value());
}
error happen if i include <LuaBridge/LuaBridge.h> in b.cpp
lua.hpp
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
a.h
#pragma once
#include <lua.hpp>
class b; // forward declaration
a.cpp
#include <a.h>
#include <LuaBridge/LuaBridge.h> // fine
#include <b.h>
// #include <LuaBridge/LuaBridge.h> // if include at here error too
b.h
#pragma once
#include <lua.hpp>
class a; // forward declaration
b.cpp
#include <b.h>
#include <a.h>
#include <LuaBridge/LuaBridge.h> // error appear if i include
I have made a LuaBridge registration of the tinyxml2
framework. The following code executes perfectly every time on LB2. But on LB3 the xml
variable is garbage collected, usually before the first iteration of the inner loop. Both the LB2 and LB3 versions are built with Lua 5.4.4, so a difference in Lua version would not seem to be the issue.
The Lua function is:
function xmlelements(node, nodename)
local child = node and node:FirstChildElement(nodename) or nil
if child then child = child:ToElement() end -- in case node is XMLHandle
return function()
local current_child = child
if child then
child = child:NextSiblingElement(nodename)
end
return current_child
end
end
local function do_xml_parse()
local xml = tinyxml2.XMLDocument()
local result = xml:LoadFile(finenv.RunningLuaFolderPath() .. "/jwluatagfile.xml")
print("load result "..tostring(result))
if result ~= tinyxml2.XML_SUCCESS then
error("Unable to find jwluatagfile.xml. Is it in the same folder with this script?")
end
local class_collection = {}
local tagfile = tinyxml2.XMLHandle(xml):FirstChildElement("tagfile"):ToNode()
for compound in xmlelements(tagfile, "compound") do
print("compound", compound:FirstChildElement("name"):GetText())
if compound:Attribute("kind", "class") then
local class_info = { _attr = { kind = 'class' }, __members = {} }
class_info.name = compound:FirstChildElement("name"):GetText()
class_info.filename = compound:FirstChildElement("filename"):GetText()
class_info.base = compound:FirstChildElement("filename"):GetText()
for member in xmlelements(compound, "member") do
print(" member", member:FirstChildElement("name"):GetText())
if member:Attribute("kind", "function") then
local member_info = { _attr = { kind = 'function' } }
member_info.type = member:FirstChildElement("type"):GetText()
member_info.name = member:FirstChildElement("name"):GetText()
member_info.anchorfile = member:FirstChildElement("anchorfile"):GetText()
member_info.anchor = member:FirstChildElement("anchor"):GetText()
member_info._attr.protection = member:Attribute("protection", nil)
member_info._attr.static = member:Attribute("static", nil)
member_info._attr.virtualness = member:Attribute("virtualness", nil)
member_info.arglist = member:FirstChildElement("arglist"):GetText()
class_info.__members[member_info.name] = member_info
end
end
class_collection[class_info.name] = class_info
end
end
xml:Clear()
print("********** exiting function *************")
return class_collection
end
An abbreviated version of the registration of XMLDocument
is here. (XMLNode
is registered before this.)
luabridge::getGlobalNamespace(L)
.beginNamespace("tinyxml2")
.deriveClass<tinyxml2::XMLDocument, tinyxml2::XMLNode>("XMLDocument")
#if USE_PROXY
.addConstructor([] (void* ptr, lua_State *L)
{
const int numArgs = lua_gettop(L);
const bool processEntities = (numArgs > 2) ? luabridge::Stack<bool>::get(L, 2).value() : true;
const tinyxml2::Whitespace whitespaceMode = (numArgs > 3)
? luabridge::Stack<tinyxml2::Whitespace>::get(L, 3).value()
: tinyxml2::PRESERVE_WHITESPACE;
return new(ptr) tinyxml2::XMLDocument(processEntities, whitespaceMode);
})
#else
.addConstructor<void (*) ()>()
#endif
// plus function registrations
.endClass()
.endNamespace();
It doesn't matter which version of the constructor I use. Lua is garbage-collecting xml
after only two print
statements. When I run this same code with LB2, it prints 100s of print statements.
I can reproduce the problem with a smaller less complicated loop, but only if I run it in a coroutine. (Providing a small coroutine example is a challenge, because to execute a coroutine in the Finale Lua environment you have to set up a callback function, usually a timer on a dialog box, which introduces a lot of code specific to Lua on Finale.) This code is failing consistently even in the main execution stream without a coroutine.
I would have thought Lua would always keep xml
alive until the function completed, especially since there is a xml:Clear()
at the bottom.
Please, add a new release or tag for project, theres a considerable number of changes since 3.0-rc1.
Asking for that due vcpkg and conan.
Error
In file included from /media/dezlow/Drive/Dev/C++/Oneiro/SandBox/Source/SandBoxApp.cpp:6:
In file included from /media/dezlow/Drive/Dev/C++/Oneiro/SandBox/Source/SandBoxApp.hpp:8:
In file included from /media/dezlow/Drive/Dev/C++/Oneiro/Engine/Include/Oneiro/Runtime/Application.hpp:8:
In file included from /media/dezlow/Drive/Dev/C++/Oneiro/Engine/Include/Oneiro/Core/Event.hpp:10:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/functional:54:
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/tuple:1852:14: error: no matching function for call to '__invoke'
return std::__invoke(std::forward<_Fn>(__f),
^~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/tuple:1863:19: note: in instantiation of function template specialization 'std::__apply_impl<oe::MainCameraComponent *(oe::World::Entity::*&)() noexcept, std::tuple<>>' requested here
return std::__apply_impl(std::forward<_Fn>(__f),
^
/media/dezlow/Drive/Dev/C++/Oneiro/ThirdParty/luabridge3/Source/./LuaBridge/detail/CFunctions.h:545:25: note: in instantiation of function template specialization 'std::apply<oe::MainCameraComponent *(oe::World::Entity::*&)() noexcept, std::tuple<>>' requested here
L, std::apply(func, make_arguments_list<ArgsPack, Start>(L)));
^
/media/dezlow/Drive/Dev/C++/Oneiro/ThirdParty/luabridge3/Source/./LuaBridge/detail/CFunctions.h:753:92: note: in instantiation of function template specialization 'luabridge::detail::function<oe::MainCameraComponent *, std::tuple<>, 1>::call<oe::MainCameraComponent *(oe::World::Entity::*)() noexcept>' requested here
return function<typename FnTraits::result_type, typename FnTraits::argument_types, 1>::call(
^
/media/dezlow/Drive/Dev/C++/Oneiro/ThirdParty/luabridge3/Source/./LuaBridge/detail/CFunctions.h:918:28: note: in instantiation of function template specialization 'luabridge::detail::invoke_proxy_functor<oe::MainCameraComponent *(oe::World::Entity::*)() noexcept>' requested here
lua_pushcclosure_x(L, &invoke_proxy_functor<F>, 1);
^
/media/dezlow/Drive/Dev/C++/Oneiro/ThirdParty/luabridge3/Source/./LuaBridge/detail/Namespace.h:841:29: note: in instantiation of function template specialization 'luabridge::detail::push_member_function<oe::World::Entity, oe::MainCameraComponent *(oe::World::Entity::*)() noexcept, std::enable_if<false>>' requested here
detail::push_member_function<T>(L, std::move(functions));
^
/media/dezlow/Drive/Dev/C++/Oneiro/SandBox/Source/SandBoxApp.cpp:62:14: note: in instantiation of function template specialization 'luabridge::Namespace::Class<oe::World::Entity>::addFunction<oe::MainCameraComponent *(oe::World::Entity::*)() noexcept>' requested here
.addFunction("AddMainCameraComponent", &World::Entity::AddComponent<MainCameraComponent>)
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:90:5: note: candidate template ignored: substitution failure [with _Callable = oe::MainCameraComponent *(oe::World::Entity::*&)() noexcept, _Args = <>]: no type named 'type' in 'std::__invoke_result<oe::MainCameraComponent *(oe::World::Entity::*&)() noexcept>'
__invoke(_Callable&& __fn, _Args&&... __args)
Hello im trying to execute a lua function when a value some_val
is changed
function a(some_val)
print(some_val.val)
end
I export a Hook
function, that i can call like this:
Hook("somehook", a)
-- can do other code here after
c++ full code:
#include <chrono>
#include <iostream>
extern "C" {
#include <lua/lauxlib.h>
#include <lua/lua.h>
#include <lua/lualib.h>
}
#include <LuaBridge3/LuaBridge.h>
namespace utils {
template <typename T = std::chrono::milliseconds>
long long GetTime() {
return std::chrono::duration_cast<T>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
}
lua_State* L = nullptr;
long long unpause_at = 0;
bool stop_resuming_main = false;
struct LUAThread {
lua_State* co = nullptr;
luabridge::LuaRef cb;
LUAThread(lua_State*&& co, luabridge::LuaRef&& cb) : co(std::move(co)), cb(std::move(cb)) {}
void KillExecution() noexcept {
lua_close(co);
}
};
std::unordered_map <std::string, LUAThread> hooks;
int some_val = 7; // just for testing, in real world it has the ability to change.
void Execute(const std::string_view& script) noexcept {
L = luaL_newstate();
luaL_openlibs(L);
// todo: make this work in any lua thread.
auto Sleep = [&](long long ms) {
unpause_at = utils::GetTime() + ms;
};
auto Hook = [&](const std::string& name, luabridge::LuaRef cb) noexcept {
lua_State* co = lua_newthread(L);
LUAThread hook = LUAThread(std::move(co), std::move(cb));
hooks.emplace(name, std::move(hook));
};
auto UnHook = [](const std::string& name) noexcept { hooks.erase(name); };
luabridge::getGlobalNamespace(L)
.addFunction("Hook", Hook)
.addFunction("UnHook", UnHook)
.addFunction("Sleep", Sleep);
luaL_loadstring(L, script.data());
auto hook_function = [](lua_State* L, lua_Debug* ar) {
lua_yield(L, 0);
return;
};
lua_sethook(L, hook_function, LUA_MASKCOUNT, 1);
}
void KillExecution() noexcept {
for (auto& hook : hooks) {
hook.second.KillExecution();
hooks.erase(hook.first);
}
lua_close(L);
}
bool HandleLUA() noexcept {
// hooks
for (auto& hook : hooks) {
auto& name = hook.first;
auto& luahook = hook.second;
luahook.cb.push(luahook.co);
lua_newtable(luahook.co);
lua_pushstring(luahook.co, "val");
lua_pushinteger(luahook.co, some_val);
lua_settable(luahook.co, -3);
int nres = 0;
int status = lua_resume(luahook.co, 0, 1, &nres);
if (status != LUA_YIELD) {
if (status == LUA_OK) {
std::cout << "ok";
luahook.KillExecution();
}
else if (lua_isstring(luahook.co, -1)) {
std::cout << "[LUA][corout] error: " << lua_tostring(luahook.co, -1) << '\n';
luahook.KillExecution();
}
}
}
// end hooks
// main
if (unpause_at <= utils::GetTime() && !stop_resuming_main) {
int nres = 0;
int status = lua_resume(L, nullptr, 0, &nres);
if (status != LUA_YIELD) {
if (status == LUA_OK) {
if (hooks.size() == 0) {
KillExecution();
return true;
}
else {
stop_resuming_main = true;
}
}
else if (lua_isstring(L, -1)) {
std::cout << "[LUA][main] error: " << lua_tostring(L, -1) << '\n';
KillExecution();
return true;
}
}
}
return false;
}
int main() {
#ifdef _DEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
Execute("function a(some_val)\n print(some_val.val)\nend\n\nHook(\"somehook\", a)");
while (!HandleLUA()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return 0;
}
But I am getting error [LUA][corout] error: cannot resume dead coroutine
when it tries to resume the hook thread.
probably because LuaRef is on main lua_State* L
so my question is, how do I move the LuaRef callback from lua_State* L
to LUAThread.co
?
Should be interesting more addStaticPropery overload like these:
template <class Getter, typename = std::enable_if_t<!std::is_pointer_v<Getter>>>
Class<T> addStaticProperty(const char* name, Getter get)
{
assert(name != nullptr);
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
using GetType = decltype(get);
lua_newuserdata_aligned<GetType>(L, std::move(get)); // Stack: co, cl, st, function userdata (ud)
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1); // Stack: co, cl, st, function
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
return *this;
}
template <class Getter, class Setter, typename = std::enable_if_t<!std::is_pointer_v<Getter> && !std::is_pointer_v<Setter>>>
Class<T> addStaticProperty(const char* name, Getter get, Setter set)
{
assert(name != nullptr);
assertStackState(); // Stack: const table (co), class table (cl), static table (st)
using GetType = decltype(get);
using SetType = decltype(set);
lua_newuserdata_aligned<GetType>(L, std::move(get)); // Stack: co, cl, st, function userdata (ud)
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<GetType>, 1); // Stack: co, cl, st, function
detail::add_property_getter(L, name, -2); // Stack: co, cl, st
lua_newuserdata_aligned<SetType>(L, std::move(set)); // Stack: co, cl, st, function userdata (ud)
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<SetType>, 1); // Stack: co, cl, st, function
detail::add_property_setter(L, name, -2); // Stack: co, cl, st
return *this;
}
These will work with:
.addStaticProperty("onUpdate", [] () { return &Engine::onUpdate; }, [] (lua_State* L) { Engine::onUpdate.add("luaFunction", L); })
Thanks
I was implementing constructors for classes with shared_ptr
as container as described here https://kunitoki.github.io/LuaBridge3/Manual#343---shared_ptr-as-container
Problem is that I have two classes, one inherits from another, and this is where things go wrong, unless I misunderstand something.
I created separate test example to verify how things work, so the code may be as follows:
class Base : public std::enable_shared_from_this<Base>
{
public:
Base(unsigned int id);
// ...
};
class Derived : public Base
{
public:
Derived(unsigned int id, const std::string& name);
// ...
};
Now, when I create binding like this for Base
:
getGlobalNamespace(L)
.beginClass<Base>("Base")
.addConstructorFrom<std::shared_ptr<Base>, void(unsigned int)>()
.endClass();
everything's okay, but when I do
getGlobalNamespace(L)
.deriveClass<Derived, Base>("Derived")
.addConstructorFrom<shared_ptr<Derived>, void(unsigned int, const string&)>()
.endClass();
I get this error:
luabridge\detail\TypeTraits.h(63,5): error G56916779: static assertion failed due to requirement 'std::is_base_of_v<std::enable_shared_from_this<Derived>, Derived>' static_assert(std::is_base_of_v<std::enable_shared_from_this<T>, T>);
First thing I tried is to make multiple inheritance for Derived
, i.e.
class Derived : public Base, public std::enable_shared_from_this<Derived>
.
Then I get this error
luabridge\detail\TypeTraits.h(69,19): error G417914D0: member 'shared_from_this' found in multiple base classes of different types return t->shared_from_this();
I google about std::enable_shared_from_this
and inheritance in general, this seems to be good discussion https://stackoverflow.com/questions/657155/how-to-enable-shared-from-this-of-both-parent-and-derived
So there I found that simplest solution could be to override shared_from_this()
in Derived
like this:
shared_ptr<Derived> Derived::shared_from_this()
{
return static_pointer_cast<Derived>(Base::shared_from_this());
}
but when I do it like this, I get std::bad_weak_ptr
thrown in runtime when I try to pass instance of Derived
into C++ function bound to Lua, more specifically, right when Base::shared_from_this()
is called.
After some experimentation, turned that that I don't need to inherit public std::enable_shared_from_this<Derived>
, but then I have to comment std::is_base_of_v
check at TypeTraits.h(63)
. And then it works.
Full source of my example is here https://www.dropbox.com/s/qplxkfmmtpbhyla/SharedFromThisTest.zip?dl=0
With TypeTraits.h(63)
line commented it builds and runs no problem. As soon as you uncomment the line, you get the static_assert
error I posted above.
I'm not sure whether I'm doing everything correct and as intended in this example. But, if compared to RefCountedObjectPtr
, it works no problem out of the box with only Base
inheriting from it. So I assume something similar should work for shared_ptr
as well.
Some project's have their own implementation of assert
.
What do you think of replacing all of them with a macro which can be overridden.
#ifndef LUABRIDGE_ASSERT
#define LUABRIDGE_ASSERT assert
#endif
Example from ImGui: https://github.com/ocornut/imgui/blob/master/imconfig.h#L19
I recently migrated from Sol2 to Luabridge3 because this library is very lightweight, specially in compiling time. But Sol2 overload feature is fantastic.
In Luabridge3 I can do overload functions all manually, like these:
.addFunction("addVertex", +[](Polygon* self, lua_State* L) -> void {
if (lua_gettop(L) != 2 && lua_gettop(L) != 3) throw luaL_error(L, "incorrect argument number");
if (lua_isnumber(L, 2) && lua_isnumber(L, 3)) self->addVertex(lua_tonumber(L, 2), lua_tonumber(L, 3));
else if (luabridge::Stack<Vector3>::isInstance(L, -1)) self->addVertex(luabridge::Stack<Vector3>::get(L, -1));
else throw luaL_error(L, "incorrect argument type");
})
.addFunction("getBone", +[](Model* self, lua_State* L) -> Bone {
if (lua_gettop(L) != 2) throw luaL_error(L, "incorrect argument number");
if (lua_isinteger(L, -1)) return self->getBone(lua_tointeger(L, -1));
if (lua_isstring(L, -1)) return self->getBone(lua_tostring(L, -1));
throw luaL_error(L, "incorrect argument type");
})
It does the job but it is not easy as sol2.
I don't have much experience in modern C++ and I do not know if it is really possible. But would be interesting a template function like this pseudocode:
template<Ret, Func1, Func2, ...>
Ret overload(lua_State *L){
if (lua_tointeger(L, 1))
Func1(lua_tointeger(L, 1));
if (lua_tostring(L, 1))
Func2(lua_tostring(L, 1));
}
And use this in Luabridge to overload:
luabridge::getGlobalNamespace (L)
.beginNamespace ("test")
.addFunction ("foo", overload<(void (*)(int))&foo, (void (*)(std::string))&foo>)
.endNamespace ();
Also I opened a question in Stackoverflow (https://stackoverflow.com/questions/73799816/how-overload-c-functions-in-lua-using-generic-function-template) about this.
I have this enum:
enum class Scaling{
FITWIDTH,
FITHEIGHT,
LETTERBOX,
CROP,
STRETCH
};
And these C++ functions:
static void setScalingMode(Scaling scalingMode);
static Scaling getScalingMode();
Lua bindings:
luabridge::getGlobalNamespace(L)
.beginNamespace("Scaling")
.addProperty("FITWIDTH", Scaling::FITWIDTH)
.addProperty("FITHEIGHT", Scaling::FITHEIGHT)
.addProperty("LETTERBOX", Scaling::LETTERBOX)
.addProperty("CROP", Scaling::CROP)
.addProperty("STRETCH", Scaling::STRETCH)
.endNamespace();
luabridge::getGlobalNamespace(L)
.beginClass<Engine>("Engine")
.addStaticFunction("setScalingMode", &Engine::setScalingMode)
.addStaticProperty("scalingMode", &Engine::getScalingMode, &Engine::setScalingMode)
.endClass();
Code in Lua:
Engine.setScalingMode(Scaling.CROP) -- not working
Engine.scalingMode = Scaling.CROP -- not working
None of them are working. Is there anyway to get these enum working in function argument?
When I call some class method (or any function) in Lua with wrong arguments I always get this generic error:
[string "lua://main.lua"]:89: Error while calling method
stack traceback:
[C]: in method 'getSide'
[string "lua://main.lua"]:89: in main chunk
But If I comment this catch clause in FuncTraits.h:
catch (...)
{
luaL_error(L, "Error while calling method");
}
I get a more specific error:
[string "lua://main.lua"]:89: bad argument #1 to 'getSide' (Vector3 expected, got table)
stack traceback:
[C]: in method 'getSide'
[string "lua://main.lua"]:89: in main chunk
Which has much more information.
Is this intentional or am I doing something wrong?
test.cpp
int main(){
lua_State *L = luaL_newstate();
luaL_openlibs(L);
try {
luabridge::LuaException::enableExceptions(L);
luaL_dofile(L, "/mnt/d/code/lua_cpp/bin/test.lua");
auto func = luabridge::getGlobal(L, "test");
auto res = func();
std::cout << res.errorMessage() << std::endl;
} catch (const luabridge::LuaException &e) {
// e.waht() is empty string
std::cerr << e.what() << std::endl;
}
lua_close(L);
}
test.lua
function test()
print("=============>>>>", i + i) -- I want to get error message
end
In my use case, I would like to proxy a fixed-size C-style array both for reading and writing. I'm not sure how hard this would be. I experimented a bit something like addArray(char const* name, TG (*get), TEG (*getIndex)(TG, int), void (*setIndex)(TG, int, TES) = 0)
with creating a lua_newuserdata on the fly when get
is called, putting the array pointer in there, and setting __index
and __newindex
to use getIndex
and setIndex
callbacks, but I'm afraid I don't know enough C++ to get it working properly.
Just putting this out here in case you have an idea and find it easy to implement, I will try to dig into this when I have time.
i have lua_state* and i need to reload script by key
how can i do this?
And here I come with another issue =)
This one was actually discovered by me some time ago, but I wasn't sure it's worth posting, now decided it probably is, because maybe it can be improved.
So, I started using LuaBridge 3 after LuaBridge 2.8, and in 2.8 version it was supposed to add Stack
specializations for enums this way vinniefalco/LuaBridge#204
When I started using LuaBridge 3, it was stated in the docs that now it's done this way:
"Automatic handling of enum types by communicating with lua through std::underlying_type_t".
And indeed quite soon I found out that you can bind enum values as namespace properties and in Lua they turn into numbers basically. And I'm talking about both usual enum
and and enum class
. So I just wrote an example to double-check that, goes this way:
enum Enum
{
ONE,
TWO,
THREE
};
enum class EnumClass
{
One,
Two,
Three
};
// Then in bindings:
getGlobalNamespace(L)
.beginNamespace("Enum")
.addProperty("ONE", ONE)
.addProperty("TWO", TWO)
.addProperty("THREE", THREE)
.endNamespace()
.beginNamespace("EnumClass")
.addProperty("One", EnumClass::One)
.addProperty("Two", EnumClass::Two)
.addProperty("Three", EnumClass::Three)
.endNamespace()
And then in Lua you can just do print(Enum.ONE)
or print(EnumClass.One)
and it will print 0.
Though here along the way I may express a wish that instead of beginNamespace()
maybe it would be better to have beginEnum()
to have more descriptive syntax, but that's minor thing.
So the problem appears when, for example, you try to pass this enum value to some function like this:
// C++
void testEnumAsArg(Enum value)
{
printf("Enum value: %i.\n", value);
}
-- Lua
testEnumAsArg(Enum.One)
Doesn't matter whether it's enum
or enum class
, it will fail with this error:
bad argument #1 to 'testEnumAsArg' (unregistered class expected, got number)
.
Then turns out that Stack
specialization works, only it needs to be updated to LuaBridge 3 specifics like Result
/ TypeResult
stuff etc. I think I'll omit code example here, I will share a link to full example again. With that wrapper I can do it like this:
template <>
struct Stack<Enum> : LuaEnumClassWrapper<Enum>
{
};
And then passing enum as argument starts working as expected.
A quite frustrating thing may happen when you don't even get any meaningful error and it just doesn't work.
That's the case when you create std::map
when enum is used as key, like this:
std::map<Enum, std::string> testEnumMap = {
{ ONE, "ONE" },
{ TWO, "TWO" },
{ THREE, "THREE" }
};
Then, if you bind this map (with the help of #include "LuaBridge/Map.h"
, of course), and try to access it in Lua, you just get an error without any description.
Though if instead you change map
declaration to this std::map<int, std::string>
it will work just fine.
So, initially I thought "well, I'm just going to use these wrappers", but now I started to think it's probably possible to do some automatic conversion here without using them. Because if it basically works C++ => Lua way, it should work the other way around.
If there are some complications, then I can continue use these wrappers just fine.
Full code example is here https://www.dropbox.com/s/ew7fahendv2z6xr/EnumClassTest.zip?dl=0. Wrappers are on line 30 of main.cpp
, if you comment them, you get errors I'm talking about.
The versions 2 and 3 are basically compatible.
There are backward incompatible differences we should address or avoid.
@kunitoki Let's discuss the migration.
File:detail/LuaHelpers.h
func:
is_integral_representable_by
is_floating_point_representable_by
Error example:
std::numeric_limits<U>::max()
std::numeric_limits<U>::min()
fix:
(std::numeric_limits<U>::max)()
(std::numeric_limits<U>::min)()
There seems to be a problem with custom memory alignment.
It is definitely a problem with LuaBridge3. It used to work in the vanilla LuaBridge with modifications to UserdataValue that I made. (See code)
Code taken from:
SteveKChiu/lua-intf#38
https://github.com/SteveKChiu/lua-intf/blob/master/LuaIntf/impl/CppObject.h#L255
typedef __m128d SSEType;
struct alignas(16) OtherClass
{
public:
SSEType XY;
SSEType ZW;
};
OtherClass __LoadAligned(const double* Ptr)
{
OtherClass Result;
Result.XY = _mm_load_pd((const double*)(Ptr));
Result.ZW = _mm_load_pd((const double*)(Ptr + 2));
return Result;
}
OtherClass __VectorCompareEQ(const OtherClass& Vec1, const OtherClass& Vec2)
{
OtherClass Result;
Result.XY = _mm_cmpeq_pd(Vec1.XY, Vec2.XY);
Result.ZW = _mm_cmpeq_pd(Vec1.ZW, Vec2.ZW);
return Result;
}
int __VectorMaskBits(const OtherClass& VecMask)
{
const int MaskXY = _mm_movemask_pd(VecMask.XY);
const int MaskZW = _mm_movemask_pd(VecMask.ZW);
return (MaskZW << 2) | (MaskXY);
}
template<typename T>
struct alignas(16) Vec
{
public:
Vec() { }
Vec(T InA, T InB, T InC, T InD)
{
X = InA;
Y = InB;
Z = InC;
W = InD;
}
public:
T X;
T Y;
T Z;
T W;
bool operator==(const Vec<T>& Q) const
{
const OtherClass A = __LoadAligned((const double*)this);
const OtherClass B = __LoadAligned((const double*)&Q);
return __VectorMaskBits(__VectorCompareEQ(A, B)) == 0x0F;
}
};
using FVec = Vec<double>;
Register in Lua:
.beginClass<FVec>("Vec")
.addConstructor<void(*) ()>()
.addConstructor<void(*) (double, double, double, double)>()
.addProperty("A", &FVec::X)
.addProperty("B", &FVec::Y)
.addProperty("C", &FVec::Z)
.addProperty("D", &FVec::W)
.addFunction("__eq", &FVec::operator==)
.endClass()
Trigger crash:
assert(Vec(3.0, 2.0, 1.0, 0.5) == Vec(3.0, 2.0, 1.0, 0.5))
The C++ code works just fine. When you run this without the Lua part it does not crash.
FVec aa(1, 2, 3, 4);
FVec bb(1, 2, 3, 4.2);
auto r = aa.operator==(bb);
Callstack:
Vec<double>::operator==()
luabridge::detail::function<bool,std::tuple<Vec<double> const &>,2>::call<Vec<double> const ,bool (__cdecl Vec<double>::*)(Vec<double> const &)const >() [LuaBridge.h:5792]
lua_yieldk()
luaD_precall()
luaM_toobig()
luaT_callTMres()
luaV_equalobj()
luaV_execute()
luaM_toobig()
luaD_rawrunprotected()
luaD_pcall()
lua_pcallk()
I am trying to bind these functions:
Vector3 Vector3::operator * ( float f ) const{
return Vector3(x * f, y * f, z * f);
}
Vector3 Vector3::operator * ( const Vector3& v ) const{
return Vector3(x * v.x, y * v.y, z * v.z);
}
Using overload feature in __mul
metamethod:
luabridge::getGlobalNamespace(L)
...
.addFunction("__mul", (Vector3 (Vector3::*)(const Vector3&) const)&Vector3::operator*, (Vector3 (Vector3::*)(float) const)&Vector3::operator*)
...
.endClass();
Testing this Lua code:
vec1 = Vector3(3,3,3)
vec2 = Vector3(2,2,2)
print(vec1 * 3)
print(vec1 * vec2)
And I'm getting this error only in print(vec1 * vec2)
line:
( ERROR ): Lua Error: All 2 overloads of mul returned an error:
1: Skipped overload #0 with unmatched arity of 0 instead of 1
2: Error decoding argument #2: The lua object can't be casted to desired type
stack traceback:
[C]: in metamethod 'mul'
[string "lua://main.lua"]:19: in main chunk
I think function arity should be 1 but is registered with 0.
Hello!
Why you don't update luau to new versions? I need to use a modern luau with bug fixes and new features, but LuaBridge written with using Luau 0.50x.
Thanks!
I noticed there are some commits on the vinniefalco/LuaBridge repository that are not integrated here. Just curious: was there a particular reason not to integrate your changes into that repository?
Thank you for your work on this project.
I have some questions and would be very thankful to have some answers.
Let's assume I have exposed multiple classes to Lua with LuaBridge3. They include simple structs like Vectors where lifetime is managed by Lua but also pointers to objects where the host manages lifetime.
I often do not know the exact type at compile time.
class A
class B : A // B inherits A
Let's say I have a pointer of type A but it's actually an instance of class B and I want to push the pointer as if it was B.
I think it would require a dynamic database or something of the class keys in ClassInfo.h.
Is something like this possible with LuaBridge3?
I would like to add functions to C++ exposed classes in Lua. Ideally one could even override existing C++ functions and also call the function of the parent class.
I know that userdata types cannot be extended in Lua but is there some other way to achieve that? Or am I wrong?
Let's assume we only expose known safe functions to Lua and forbid bytecode. Did you do some research or tests on LuaBridge3 whether it is safe to run unknown Lua code.
Would be nice if you could share your plan on features etc. you might be working on in the future.
Thank you again for your work and time.
// problem
UserdataValue<T>(const UserdataValue<T>&);
UserdataValue<T> operator=(const UserdataValue<T>&);
// solution
UserdataValue(const UserdataValue&);
UserdataValue operator=(const UserdataValue&);
I was able to figure out the solution per stackoverflow answer:
https://stackoverflow.com/a/67698585
I would like to use LuaBride3 in a c++ 32-bit windows project with the Embarcadero C++ Builder (Community).
Just including "LuaBridge3.h" stops compilation in line 2176: redefinition of 'Stack<char,void>'
template <>
struct Stack<int8_t>
{
Previous line here: 2128
template <>
struct Stack<char>
{
Example code (VCL application):
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "mainform.h"
#include "lua5.4/lua.hpp"
#include "LuaBridge/LuaBridge.h"
#include <iostream>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
//
}
I assume C++ Builder treats int8_t
the same as char
.
Do you know of a flag or project setting I can change to fix the error?
I have a C++ Object like this:
class Object:RefCountedObject
{
Object();
~Object();
}
In Lua script:
local o = Object()
error("generate an error")
...
The destructor did not called when Lua script finished.
How to solve this problem?
The method will trigger the panic handler if a cast cannot be made, this is fine when running in lua, but when executed from C++ it aborts the application when building without exceptions.
Suggestion is to have Userdata::get
to return a TypeResult<T*>
instead of T*
(and eventually making TypeResult
to hold an error string).
The latest LuaBridge2 supports using vcpkg (https://vcpkg.info/port/luabridge).
Are there any plans to also include luabridge3?
For some reason, it works only after including <limits>
. Everything else works fine.
table
.It should rather print that it got no value
.
[string "testscript"]:X: bad argument #1 to 'MyFunction' (SOMETYPE expected, got table)
Problem lies around Userdata::throwBadArg
, it prints table because the metatable of the userdata is on the stack.
I have started experimenting with .addConstructor ([] (void* ptr, lua_State* L)
proxy constructors. This feature is one of the principal factors driving me to migrate to LB3 from LB2.
I ran into a problem that sometimes my proxy constructor works and sometimes it doesn't. (When it doesn't, the program often crashes. Basically my constructed Lua userdata is pointing at garbage, so the results are unpredictable.)
I'm fairly certain the cause is that I am constructing the instance inside a Lua coroutine. If this is enough information to go on, I'll leave it at that. Otherwise, I'll dig further. Let me know.
See below for isolated test case. The coroutine seems to be an aggravating factor, but this test fails nearly consistently in the main routine.
Hi,
I saw that addConstant was renamed to addVariable. I was wondering if there's a particular reason not to have an addConstant
(or maybe addValue
). My use case is to expose some global #define
and enum
values as names to Lua. Since those will never change, I was wondering if it wouldn't be more efficient to just have this available at least for primitive types:
template <class T>
Namespace& addConstant(char const* name, T value)
{
assert(name != nullptr);
assert(lua_istable(L, -1)); // Stack: namespace table (ns)
Stack::push(L, name); // Stack: ns, key
Stack::push(L, value); // Stack: ns, key, value
lua_settable(L, -3);
return *this;
}
When i try add function in table, give me null error ('C:\scripts\Example.lua:11:' and no more)
getGlobalNamespace(L)
.beginNamespace("Entities")
.addFunction("GetLocalHero", [&]() {
auto table = luabridge::newTable(L);
table["index"] = 150;
table["Health"] = [&]() {
return 500;
};
return table;
})
.endNamespace();
When i delete table["health"], everything is okay, but when i try to bind function in table, something is wrong
in lua:
cout(tostring(Entities.GetLocalHero().Health()))
lua version 5.4.2, luabridge: "LuaBridge3-master.zip"
trying to make a function exposed to lua that can add custom code to function
std::map<string, std::function<void(int, string)>> hooklist;
void Hook(std::string name, std::function<void(int, std::string)> cb) {
hooklist.emplace(name, cb);
}
lua_State* luaState = luaL_newstate();
luaL_openlibs(luaState);
luabridge::getGlobalNamespace(luaState).addFunction("Hook", &Hook);
luaL_dofile(luaState, get_path(fileToEdit).string().c_str());
then in the function i'm "hooking"
for (auto& func : hooklist) {
func.second(type, packet);
}
Lua script i did for test:
function hook1(type, packet)
log("lol")
end
Hook("hook1", hook1)
But if i try add debug statement in Hook() even it doesn't print, but if i remove std::function<void(int, string)>
argument it works, but the whole purpose of code loses value. How could I make this work?
In 2.6.1 - Constructor Proxies, all constructors that use functions are missing return
statements, however these are necessary.
Otherwise works nicely, thanks
Consider this piece of code
struct Vec
{
public:
Vec operator+(const Vec& v) const
{
Vec a;
return a;
}
template<typename FArg, typename std::enable_if_t<std::is_arithmetic<FArg>::value, int> = 0>
Vec operator+(FArg v) const
{
Vec a;
return a;
}
};
luabridge::getGlobalNamespace(L)
.beginNamespace("test")
.beginClass<Vec>("Vec")
.addFunction("__add",
luabridge::constOverload<double>(&Vec::operator+<double>),
luabridge::constOverload<const Vec&>(&Vec::operator+))
.endClass()
.endNamespace()
;
Gives this compiler output:
error C3889: call to object of class type 'luabridge::detail::const_overload<const Vec &>': no matching call operator found
note: could be 'unknown-type luabridge::detail::const_overload<const Vec &>::operator ()(R (__cdecl T::* )(const Vec &) const) noexcept const'
note: 'unknown-type luabridge::detail::const_overload<const Vec &>::operator ()(R (__cdecl T::* )(const Vec &) const) noexcept const': could not deduce template argument for 'R'
note: 'unknown-type luabridge::detail::const_overload<const Vec &>::operator ()(R (__cdecl T::* )(const Vec &) const) noexcept const': could not deduce template argument for 'T'
Compiler
Visual Studio 2022 14.35.32215 toolchain
Windows 10.0.18362.0 SDK
double overload succeeds but const Vec& spec fails.
Workaround fix:
.addFunction("__add", (Vec(Vec::*)(const Vec&) const)(&Vec::operator+), (Vec(Vec::*)(double) const)(&Vec::operator+))
Stack<bool>::get
does not check whether an actual value exists on stack. This results in LuaBridge silently treating a no value
as a valid bool and not throwing any error.
See for example Stack<float>
which has a check and reports if a non-float value (or no value) is on stack.
I have begun to investigate migrating my project from LB2 to LB3, and my regression test script (which is extensive) has surfaced exactly two issues that are extremely puzzling and random. One of them I haven't even thought of how to investigate, so I am tackling the other first. (Perhaps they have the same root cause: a man can dream.) I don't have a reproducible test case, but I was hoping @kunitoki or anyone else might have an idea as to where to start digging.
I have a class that (apparently) due to its name is generating a runtime error in LB3 but not in LB2. All my efforts to identify this problem have failed. Any ideas for how to proceed would be most welcome.
My problem class is in an inheritance tree similar to the following. (The following works. I am just providing it for context so that I can describe the technical details of the problem.)
struct basebase {};
struct barbase : basebase
{
virtual int foobar(int a, int b) { return a + b; }
};
struct foobase : basebase
{
virtual int foobar(int a) { return a; }
};
struct foo : foobase {};
And it is registered like this:
luabridge::getGlobalNamespace(L)
.beginNamespace("foobar")
.beginClass<basebase>("basebase")
.endClass()
.deriveClass<barbase, basebase>("barbase")
.addFunction("foobar", &barbase::foobar)
.endClass()
.deriveClass<foobase, basebase>("foobase")
.addFunction("foobar", &foobase::foobar)
.endClass()
.deriveClass<foo, foobase>("foo")
.addConstructor<void(*) (void)>()
.endClass()
.endNamespace();
With my class (not the above example), when I run this Lua code, it fails as shown:
local foo = foobar.foo()
local rslt = foo:foobar(3)
--runtime error: Error decoding argument #3: The lua object can't be cast to desired type
I have done extensive debug tracing, and here is what I know:
foobar()
is failing because when Lua retrieves its function pointer from the Lua stack, the pointer has been miscast to barbase::foobar
which takes two arguments instead of one.foobase::foobar
) is called (which only sees the first argument).addFunction
to confirm this.)I have dozens of other classes registered in exactly this same manner. None of them (that I've found) have this problem. It is something specific to this class.
FCStaffSystem
) does not exhibit the problem.This problem is related to the actual class name and not its registered name in LB. But why this particular class name is causing this failure has stumped me.
So from the manual it reads as if any pointers returned to Lua will never be collected by Lua (delete will not be called on them), but copy-constructed objects will be. Is there any better way to control this? I want to be able to define two functions, and both should return a pointer to a newly constructed object. But one of the two objects is expected to be deleted by the C++ code later, and the other should be deleted when Lua decides to collect it. Is that possible, or am I thinking about this wrong?
Here I am again, and this time it's more of a question rather than an issue.
Let's get straight to practical example with this one. So, there may be various needs to store Lua functions as LuaRef
s on the C++ side, but I guess one of most common reasons is to establish some event subscription from scripts to events happening in C++.
A naive example may look like this:
static std::set<LuaRef> eventHandlers;
static void subscribe(LuaRef handler)
{
if (handler.isFunction())
{
eventHandlers.insert(handler);
}
}
static void trigger()
{
for (auto handler : eventHandlers)
{
handler();
}
}
int main()
{
const auto L = getLuaState();
registerMainThread(L);
enableExceptions(L);
getGlobalNamespace(L)
.addFunction("subscribe", subscribe)
.addFunction("trigger", trigger);
loadScript("test.lua");
trigger();
return 0;
}
And then in Lua:
subscribe(function()
print('Hello')
end)
subscribe(function()
print('World')
end)
And here first function is added okay, but as soon as another one is attempted to be added, I get this error: attempt to compare two function values
. As I understand, that's because internally std::set
attempts to check function uniqueness compared to already added to that set and that gets to comparison of LuaRef
s and somewhere inside LuaBridge code this is determined as incorrect operation.
Ability to compare functions is important in this concept, because as soon as you need to remove a function from event handlers collection, you need some way to identify this function. In Lua functions may be compared easily because I assume they're some kind of pointers, i.e. function variable can be printed as function: 0000024a923ade90
and thus same such values may be considered equal. So it's possible to store event handlers in Lua in form of table where function itself may even be a table key, so it can be like:
handlers = {}
local func = function()
print('Hello')
end
-- subscribe
handlers[func] = func
-- unsubscribe
handlers[func] = nil
In C++, as I understand, we don't have a simple possibility to compare LuaRef
s which are actually referencing function, is this correct?
In my code I already implemented a workaround where I have special Function
class that wraps LuaRef
, has unique ID per instance, and provides operator==
that compares IDs of passed instances, so it can be used in std::set
.
But this means in scripts I have to additionally wrap Lua function into that Function
class I bind. So it's like:
local handler = Function(function()
print('Hello')
end))
-- then I can pass `handler` to C++
But this means that wrapping everywhere in scripts. Maybe more elegant solution is possible?
This a follow-on to issue #82.
Suppose you have this C method registered in Lua:
int my_method(bool arg);
With LB2, any of the following calls in Lua leads to my_method
being called with arg == false
in C++.
my_method(false)
my_method(nil)
my_method()
I currently have a constructor function with a boolean parameter that is omitted by the Lua code. LB3 is calling the constructor with true
rather than false
! (!!!!)
This is perhaps the most wrong of any possible choice. I see only two possible acceptable behaviors if a boolean parameter is missing or nil:
false
to the C function like LB2 does.If I were starting from scratch I might vote for option 2. But my project has been going a long time with the assumption of option 1. Many of the constructors and methods have trailing optional boolean parameters that C++ defaults to false
. The option 1 behavior allows those trailing booleans also to be optional in Lua for free.
For my project, switching to LuaBridge3 (which I would very much like to do) is contingent on no breaking changes to the many thousands of lines of code written for Lua on Finale over the last nearly 20 years. Option 2 would be an unacceptable breaking change. I'm a believer in flexibility and options, so perhaps the choice between 1 & 2 should be both well-defined and documented (which I don't think it is at the moment) and selectable by means of a compile-time macro.
I took a stab at migrating my project from LuaBridge2 to LuaBridge3. I ran into a couple of roadblocks:
std::shared_ptr
, but it is not, in fact, non-intrusive. Per the manual, each such class has to derive std::enable_shared_from_this
, unless I am missing something. Is there any reason I can't use RefCountedPtr
from LuaBridge2? It seems like it should work without any changes.Namespace::addCFunction
appears to be missing. (Or at least that's the error message I'm getting.)luabridge::Stack<bool>::push(L, returnVal);
What should I be doing with the return value? Or is there a more proper way to push values?
Using the current 0970f74 LuaBridge.h distribution file I get the error.
[string "testscript"]:10: bad argument #2 to 'Equals' (unregistered class expected, got nil)
Q1 = Quat(10, 90, 200, 1)
T = Transform.Identity
T:SetTranslation(Vector(10.0, 20.0, 30.0))
T:SetRotation(Q1)
R1 = T * T
R2 = Transform(Quat(3.0, 6.0, 9.0, -11.75), Vector(-71.0, 128.0, 27.0), Vector(1.690, 0.640, 0.810));
print("R1", R1)
print("R2", R2)
assert(R1:Equals(R2) == true);
I am sorry but I can't give any more C++ code to reproduce. All I can say is that R1
and R2
are valid Transform types and Equals
expects a transform. These are test files that all worked with vanilla Luabridge.
Func signature:
.addFunction("Equals", [](const Transform* c, const Transform& Other, std::optional<double> Tolerance) -> bool {} )
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.