update examples, support inherited usertype metatables, and beef up wording and fix other crap in docs

Addresses #157 temporarily
This commit is contained in:
ThePhD 2016-08-10 20:39:30 -04:00
parent 4aea285769
commit 5460f7e626
20 changed files with 637 additions and 112 deletions

View File

@ -132,6 +132,24 @@ if 'win32' in sys.platform:
else:
tests = os.path.join(builddir, 'tests')
tests_inputs = []
tests_object_files = []
for f in glob.glob('test*.cpp'):
obj = object_file(f)
tests_inputs.append(f)
tests_object_files.append(obj)
examples = []
examples_input = []
for f in glob.glob('examples/*.cpp'):
if 'win32' in sys.platform:
example = os.path.join(builddir, replace_extension(f, '.exe'))
else:
example = os.path.join(builddir, replace_extension(f, ''))
examples_input.append(f)
examples.append(example)
# ninja file
ninja = ninja_syntax.Writer(open('build.ninja', 'w'))
@ -149,7 +167,8 @@ ninja.rule('compile', command = '$cxx -MMD -MF $out.d -c $cxxflags -Werror $in -
deps = 'gcc', depfile = '$out.d',
description = 'Compiling $in to $out')
ninja.rule('link', command = '$cxx $cxxflags $in -o $out $ldflags', description = 'Creating $out')
ninja.rule('runner', command = tests)
ninja.rule('tests_runner', command = tests)
ninja.rule('examples_runner', command = 'cmd /c ' + (' && '.join(examples)) if 'win32' in sys.platform else ' && '.join(examples) )
ninja.rule('example', command = '$cxx $cxxflags $in -o $out $ldflags')
ninja.rule('installer', command = copy_command)
ninja.rule('uninstaller', command = remove_command)
@ -158,22 +177,17 @@ ninja.newline()
# builds
ninja.build('build.ninja', 'bootstrap', implicit = sys.argv[0])
tests_object_files = []
for f in glob.glob('test*.cpp'):
obj = object_file(f)
tests_object_files.append(obj)
for obj, f in zip(tests_object_files, tests_inputs):
ninja.build(obj, 'compile', inputs = f)
examples = []
for f in glob.glob('examples/*.cpp'):
example = os.path.join(builddir, replace_extension(f, ''))
examples.append(example)
for example, f in zip(examples, examples_input):
ninja.build(example, 'example', inputs = f)
ninja.build(tests, 'link', inputs = tests_object_files)
ninja.build('tests', 'phony', inputs = tests)
ninja.build('examples', 'phony', inputs = examples)
ninja.build('install', 'installer', inputs = args.install_dir)
ninja.build('uninstall', 'uninstaller')
ninja.build('examples', 'phony', inputs = examples)
ninja.build('run', 'runner', implicit = 'tests')
ninja.default('run')
ninja.build('run', 'tests_runner', implicit = 'tests')
ninja.build('run_examples', 'examples_runner', implicit = 'examples')
ninja.default('run run_examples')

View File

@ -7,6 +7,8 @@ The sole purpose of this tagging type is to work with :doc:`usertypes<usertype>`
.. code-block:: cpp
#include <sol.hpp>
struct test {
static int muh_variable;
};
@ -19,10 +21,10 @@ The sole purpose of this tagging type is to work with :doc:`usertypes<usertype>`
lua.new_usertype<test>("test",
"direct", sol::var(2),
"global", sol::var(test::muh_variable),
"ref_global", sol::var(std::ref(test::muh_variable)),
"ref_global", sol::var(std::ref(test::muh_variable))
);
int direct_value = lua["test"]["straight"];
int direct_value = lua["test"]["direct"];
// direct_value == 2
int global = lua["test"]["global"];

View File

@ -8,13 +8,17 @@ transparent argument to deal with multiple parameters to a function
struct variadic_args;
This class is meant to represent every single argument at its current index and beyond in a function list. It does not increment the argument count and is thus transparent. You can place it anyway in the argument list, and it will represent all of the objects in a function call that come after it, whether they are listed explicitly or not.
This class is meant to represent every single argument at its current index and beyond in a function list. It does not increment the argument count and is thus transparent. You can place it anywhere in the argument list, and it will represent all of the objects in a function call that come after it, whether they are listed explicitly or not.
``variadic_args`` also has ``begin()`` and ``end()`` functions that return (almost) random-acess iterators. These return a proxy type that can be implicitly converted, much like the :doc:`table proxy type<proxy>`.
.. code-block:: cpp
:linenos:
#include <sol.hpp>
int main () {
sol::state lua;
// Function requires 2 arguments
@ -22,7 +26,7 @@ This class is meant to represent every single argument at its current index and
// va will include everything after "a" argument,
// which means "b" will be part of the varaidic_args list too
// at position 0
lua.set_function("v", [](sol::this_state, int a, sol::variadic_args va, int b) {
lua.set_function("v", [](int a, sol::variadic_args va, int b) {
int r = 0;
for (auto v : va) {
int value = v; // get argument out (implicit conversion)
@ -42,3 +46,4 @@ This class is meant to represent every single argument at its current index and
lua.script("print(x)"); // 50
lua.script("print(x2)"); // 600
lua.script("print(x3)"); // 21
}

View File

@ -77,7 +77,7 @@ This is the base formula that you can follow to extend to your own classes. Usin
#include <sol.hpp>
#include <two_things.hpp>
int main (int argc, char* argv[]) {
int main () {
sol::state lua;

View File

@ -39,7 +39,7 @@ Take this ``player`` struct in C++ in a header file:
return true;
}
int set_hp(int value) {
void set_hp(int value) {
hp = value;
}
@ -49,7 +49,7 @@ Take this ``player`` struct in C++ in a header file:
private:
int hp;
}
};
It's a fairly minimal class, but we don't want to have to rewrite this with metatables in Lua. We want this to be part of Lua easily. The following is the Lua code that we'd like to have work properly:
@ -92,6 +92,9 @@ To do this, you bind things using the ``new_usertype`` and ``set_usertype`` meth
.. code-block:: cpp
:caption: player_script.cpp
#include <sol.hpp>
int main () {
sol::state lua;
// note that you can set a
@ -123,6 +126,7 @@ To do this, you bind things using the ``new_usertype`` and ``set_usertype`` meth
);
lua.script_file("player_script.lua");
}
That script should run fine now, and you can observe and play around with the values. Even more stuff :doc:`you can do<../api/usertype>` is described elsewhere, like initializer functions (private constructors / destructors support), "static" functions callable with ``name.my_function( ... )``, and overloaded member functions. You can even bind global variables (even by reference with ``std::ref``) with ``sol::var``. There's a lot to try out!

View File

@ -214,6 +214,7 @@ You can get anything that's a callable in Lua, including C++ functions you bind
function f (a)
if a < 0 then
error("negative number detected")
end
return a + 5
end
)");
@ -224,7 +225,7 @@ You can get anything that's a callable in Lua, including C++ functions you bind
sol::protected_function_result result = f(-500);
if (result.valid()) {
// Call succeeded
int x = r1esult;
int x = result;
}
else {
// Call failed
@ -269,6 +270,7 @@ You can also return mutiple items yourself from a C++-bound function. Here, we'r
:caption: Multiple returns into Lua
:name: multi-return-cxx-functions
int main () {
sol::state lua;
lua["f"] = [](int a, int b, sol::object c) {
@ -289,6 +291,7 @@ You can also return mutiple items yourself from a C++-bound function. Here, we'r
// a == 1
// b == 2
// c == "meow"
}
Note here that we use :doc:`sol::object<../api/object>` to transport through "any value" that can come from Lua. You can also use ``sol::make_object`` to create an object from some value, so that it can be returned into Lua as well.
@ -307,7 +310,7 @@ It can be used like so, inconjunction with ``sol::this_state``:
:name: object-return-cxx-functions
sol::object fancy_func (sol::object a, sol::object b, sol::this_state s) {
sol::state_view lua = s;
sol::state_view lua(s);
if (a.is<int>() && b.is<int>()) {
return sol::make_object(lua, a.as<int>() + b.as<int>());
}
@ -318,6 +321,7 @@ It can be used like so, inconjunction with ``sol::this_state``:
return sol::make_object(lua, sol::nil);
}
int main () {
sol::state lua;
lua["f"] = fancy_func;
@ -331,6 +335,7 @@ It can be used like so, inconjunction with ``sol::this_state``:
lua.script("result3 = f(true, 5.5)");
double result3 = lua["result3"];
// result3 == 16.5
}
This covers almost everything you need to know about Functions and how they interact with Sol. For some advanced tricks and neat things, check out :doc:`sol::this_state<../api/this_state>` and :doc:`sol::variadic_args<../api/variadic_args>`. The next stop in this tutorial is about :doc:`C++ types (usertypes) in Lua<cxx-in-lua>`!

41
examples/any_return.cpp Normal file
View File

@ -0,0 +1,41 @@
#include <sol.hpp>
#include <iostream>
// Uses some of the fancier bits of sol2, including the "transparent argument",
// sol::this_state, which gets the current state and does not increment
// function arguments
sol::object fancy_func(sol::object a, sol::object b, sol::this_state s) {
sol::state_view lua(s);
if (a.is<int>() && b.is<int>()) {
return sol::make_object(lua, a.as<int>() + b.as<int>());
}
else if (a.is<bool>()) {
bool do_triple = a.as<bool>();
return sol::make_object(lua, b.as<double>() * (do_triple ? 3 : 1));
}
return sol::make_object(lua, sol::nil);
}
int main() {
sol::state lua;
lua["f"] = fancy_func;
int result = lua["f"](1, 2);
// result == 3
double result2 = lua["f"](false, 2.5);
// result2 == 2.5
// call in Lua, get result
// notice we only need 2 arguments here, not 3 (sol::this_state is transparent)
lua.script("result3 = f(true, 5.5)");
double result3 = lua["result3"];
// result3 == 16.5
std::cout << "=== any_return example ===" << std::endl;
std::cout << "result : " << result << std::endl;
std::cout << "result2: " << result2 << std::endl;
std::cout << "result3: " << result3 << std::endl;
std::cout << std::endl;
}

View File

@ -1,5 +1,7 @@
#include <sol.hpp>
#include <iostream>
int main() {
// create an empty lua state
sol::state lua;
@ -10,5 +12,8 @@ int main() {
lua.open_libraries(sol::lib::base);
// call lua code directly
std::cout << "=== basic example ===" << std::endl;
lua.script("print('hello world')");
std::cout << std::endl;
}

View File

@ -19,9 +19,17 @@ struct config {
int main() {
sol::state lua;
config screen;
lua.script_file("config.lua");
// To use the file, uncomment here and make sure it is in local dir
//lua.script_file("config.lua");
lua.script(R"(
name = "Asus"
width = 1920
height = 1080
)");
screen.name = lua.get<std::string>("name");
screen.width = lua.get<int>("width");
screen.height = lua.get<int>("height");
std::cout << "=== config example ===" << std::endl;
screen.print();
std::cout << std::endl;
}

View File

@ -0,0 +1,80 @@
#include <sol.hpp>
#include <iostream>
#include <iomanip>
struct two_things {
int a;
bool b;
};
namespace sol {
// First, the expected size
// Specialization of a struct
template <>
struct lua_size<two_things> : std::integral_constant<int, 2> {};
// Now, specialize various stack structures
namespace stack {
template <>
struct checker<two_things> {
template <typename Handler>
static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
// Check first and second second index for being the proper types
bool success = stack::check<int>(L, index, handler)
&& stack::check<bool>(L, index + 1, handler);
tracking.use(2);
return success;
}
};
template <>
struct getter<two_things> {
static two_things get(lua_State* L, int index, record& tracking) {
// Get the first element
int a = stack::get<int>(L, index);
// Get the second element,
// in the +1 position from the first
bool b = stack::get<bool>(L, index + 1);
// we use 2 slots, each of the previous takes 1
tracking.use(2);
return two_things{ a, b };
}
};
template <>
struct pusher<two_things> {
static int push(lua_State* L, const two_things& things) {
int amount = stack::push(L, things.a);
amount += stack::push(L, things.b);
// Return 2 things
return amount;
}
};
}
}
int main() {
sol::state lua;
// Create a pass-through style of function
lua.script("function f ( a, b ) return a, b end");
// get the function out of Lua
sol::function f = lua["f"];
two_things things = f(two_things{ 24, true });
// things.a == 24
// things.b == true
std::cout << "=== customization example ===" << std::endl;
std::cout << std::boolalpha;
std::cout << "things.a: " << things.a << std::endl;
std::cout << "things.b: " << things.b << std::endl;
std::cout << std::endl;
return 0;
}

View File

@ -21,6 +21,8 @@ inline std::string make_string( std::string input ) {
}
int main() {
std::cout << "=== functions example ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
@ -40,7 +42,7 @@ int main() {
lua.script("assert(mult_by_ten(50) == 500)");
lua.script("assert(mult_by_five(10) == 50)");
// using lambdas, functions could have state.
// using lambdas, functions can have state.
int x = 0;
lua.set_function("inc", [&x]() { x += 10; });
@ -53,7 +55,11 @@ int main() {
}
// this can be done as many times as you want
lua.script("inc()\ninc()\ninc()");
lua.script(R"(
inc()
inc()
inc()
)");
assert(x == 40);
if (x == 40) {
// Do something based on this information
@ -63,9 +69,11 @@ int main() {
// to other variables, using sol::function
sol::function add = lua["my_add"];
int value = add(10, 11);
assert(add.call<int>(10, 11) == 21);
// second way to call the function
int value2 = add.call<int>(10, 11);
assert(value == 21);
if (value == 21) {
assert(value2 == 21);
if (value == 21 && value2 == 21) {
std::cout << "Woo, it's 21!" << std::endl;
}
@ -78,7 +86,7 @@ int main() {
auto multi = lua.get<sol::function>("multi");
int first;
std::string second;
std::tie(first, second) = multi.call<int, std::string>();
sol::tie(first, second) = multi();
// use the values
assert(first == 10);
@ -97,4 +105,6 @@ print(func(1))
print(func("bark"))
print(func(1,2))
)");
std::cout << std::endl;
}

View File

@ -0,0 +1,29 @@
-- call single argument integer constructor
p1 = player.new(2)
-- p2 is still here from being
-- set with lua["p2"] = player(0); below
local p2shoots = p2:shoot()
assert(not p2shoots)
-- had 0 ammo
-- set variable property setter
p1.hp = 545;
-- get variable through property getter
print(p1.hp);
local did_shoot_1 = p1:shoot()
print(did_shoot_1)
print(p1.bullets)
local did_shoot_2 = p1:shoot()
print(did_shoot_2)
print(p1.bullets)
local did_shoot_3 = p1:shoot()
print(did_shoot_3)
-- can read
print(p1.bullets)
-- would error: is a readonly variable, cannot write
-- p1.bullets = 20
p1:boost()

View File

@ -0,0 +1,47 @@
#include <sol.hpp>
#include <iostream>
int main() {
std::cout << "=== protected_functions example ===" << std::endl;
sol::state lua;
// A complicated function which can error out
// We define both in terms of Lua code
lua.script(R"(
function handler (message)
return "Handled this message: " .. message
end
function f (a)
if a < 0 then
error("negative number detected")
end
return a + 5
end
)");
// Get a protected function out of Lua
sol::protected_function f = lua["f"];
// Set a non-default error handler
f.error_handler = lua["handler"];
sol::protected_function_result result = f(-500);
if (result.valid()) {
// Call succeeded
int x = result;
std::cout << x << std::endl;
}
else {
// Call failed
sol::error err = result;
std::string what = err.what();
std::cout << what << std::endl;
// 'what' Should read
// "Handled this message: negative number detected"
}
std::cout << std::endl;
}

View File

@ -0,0 +1,54 @@
#include <sol.hpp>
#include <iostream>
struct test {
static int muh_variable;
};
int test::muh_variable = 25;
int main() {
std::cout << "=== static_variables example ===" << std::endl;
sol::state lua;
lua.open_libraries();
lua.new_usertype<test>("test",
"direct", sol::var(2),
"global", sol::var(test::muh_variable),
"ref_global", sol::var(std::ref(test::muh_variable))
);
int direct_value = lua["test"]["direct"];
std::cout << "direct_value: " << direct_value << std::endl;
// direct_value == 2
int global = lua["test"]["global"];
// global == 25
int global2 = lua["test"]["ref_global"];
// global2 == 25
std::cout << "First round of values --" << std::endl;
std::cout << global << std::endl;
std::cout << global2 << std::endl;
test::muh_variable = 542;
global = lua["test"]["global"];
// global == 25
// global is its own memory: was passed by value
global2 = lua["test"]["ref_global"];
// global2 == 542
// global2 was passed through std::ref
// global2 holds a reference to muh_variable
// if muh_variable goes out of scope or is deleted
// problems could arise, so be careful!
std::cout << "Second round of values --" << std::endl;
std::cout << "global : " << global << std::endl;
std::cout << "global2: " << global2 << std::endl;
std::cout << std::endl;
return 0;
}

View File

@ -5,6 +5,8 @@
// this example shows how to read data in from a lua table
int main() {
std::cout << "=== tables example ===" << std::endl;
sol::state lua;
// table used as an array
lua.script("table1 = {\"hello\", \"table\"}");
@ -52,4 +54,6 @@ int main() {
<< lua.get<sol::table>("table2").get<sol::table>("nestedTable").get<std::string>("key2")
<< '\n';
std::cout << "name of t2: " << t2.get<std::string>("name") << '\n';
std::cout << std::endl;
}

View File

@ -39,6 +39,8 @@ struct variables {
};
int main() {
std::cout << "=== usertype example ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::math);
@ -101,4 +103,7 @@ int main() {
"vars.low_gravity = true\n"
"local x = vars.low_gravity\n"
"assert(x)");
std::cout << std::endl;
}

View File

@ -0,0 +1,119 @@
#include <sol.hpp>
#include <iostream>
struct player {
public:
int bullets;
int speed;
player()
: player(3, 100) {
}
player(int ammo)
: player(ammo, 100) {
}
player(int ammo, int hitpoints)
: bullets(ammo), hp(hitpoints) {
}
void boost() {
speed += 10;
}
bool shoot() {
if (bullets < 1)
return false;
--bullets;
return true;
}
void set_hp(int value) {
hp = value;
}
int get_hp() const {
return hp;
}
private:
int hp;
};
int main() {
sol::state lua;
lua.open_libraries(sol::lib::base);
// note that you can set a
// userdata before you register a usertype,
// and it will still carry
// the right metatable if you register it later
// set a variable "p2" of type "player" with 0 ammo
lua["p2"] = player(0);
// make usertype metatable
lua.new_usertype<player>("player",
// 3 constructors
sol::constructors<sol::types<>, sol::types<int>, sol::types<int, int>>(),
// typical member function that returns a variable
"shoot", &player::shoot,
// typical member function
"boost", &player::boost,
// gets or set the value using member variable syntax
"hp", sol::property(&player::get_hp, &player::set_hp),
// read and write variable
"speed", &player::speed,
// can only read from, not write to
"bullets", sol::readonly(&player::bullets)
);
std::string player_script = R"(
-- call single argument integer constructor
p1 = player.new(2)
-- p2 is still here from being
-- set with lua["p2"] = player(0); below
local p2shoots = p2:shoot()
assert(not p2shoots)
-- had 0 ammo
-- set variable property setter
p1.hp = 545;
-- get variable through property getter
print(p1.hp);
local did_shoot_1 = p1:shoot()
print(did_shoot_1)
print(p1.bullets)
local did_shoot_2 = p1:shoot()
print(did_shoot_2)
print(p1.bullets)
local did_shoot_3 = p1:shoot()
print(did_shoot_3)
-- can read
print(p1.bullets)
-- would error: is a readonly variable, cannot write
-- p1.bullets = 20
p1:boost()
)";
// Uncomment and use the file to try that out, too!
// Make sure it's in the local directory of the executable after you build, or adjust the filename path
// Or whatever else you like!
//lua.script_file("player_script.lua");
lua.script(player_script);
std::cout << std::endl;
}

View File

@ -2,6 +2,8 @@
#include <iostream>
int main() {
std::cout << "=== variables example ===" << std::endl;
sol::state lua;
// need the base library for assertions
@ -31,4 +33,5 @@ int main() {
std::cout << y << std::endl;
std::cout << x2 << std::endl;
std::cout << y2 << std::endl;
std::cout << std::endl;
}

View File

@ -0,0 +1,37 @@
#include <sol.hpp>
#include <iostream>
int main() {
std::cout << "=== variadic_args example ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
// Function requires 2 arguments
// rest can be variadic, but:
// va will include everything after "a" argument,
// which means "b" will be part of the varaidic_args list too
// at position 0
lua.set_function("v", [](int a, sol::variadic_args va, int /*b*/) {
int r = 0;
for (auto v : va) {
int value = v; // get argument out (implicit conversion)
// can also do int v = va.get<int>(i); with index i
r += value;
}
// Only have to add a, b was included from variadic_args and beyond
return r + a;
});
lua.script("x = v(25, 25)");
lua.script("x2 = v(25, 25, 100, 50, 250, 150)");
lua.script("x3 = v(1, 2, 3, 4, 5, 6)");
// will error: not enough arguments
//lua.script("x4 = v(1)");
lua.script("print(x)"); // 50
lua.script("print(x2)"); // 600
lua.script("print(x3)"); // 21
std::cout << std::endl;
}

View File

@ -1340,3 +1340,56 @@ t.global = 20
print(t.global)
)"));
}
TEST_CASE("usertype/inheritance", "test that metatables are properly inherited") {
struct A {
int a = 5;
};
struct B {
int b() {
return 10;
}
};
struct C : B, A {
double c = 2.4;
};
struct D : C {
bool d() const {
return true;
}
};
sol::state lua;
lua.new_usertype<A>("A",
"a", &A::a
);
lua.new_usertype<B>("B",
"b", &B::b
);
lua.new_usertype<C>("C",
"c", &C::c,
sol::base_classes, sol::bases<B, A>()
);
lua.new_usertype<D>("D",
"d", &D::d,
sol::base_classes, sol::bases<C, B, A>()
);
lua.script("obj = D.new()");
lua.script("d = obj:d()");
bool d = lua["d"];
lua.script("c = obj.c");
double c = lua["c"];
lua.script("b = obj:b()");
int b = lua["b"];
lua.script("a = obj.a");
int a = lua["a"];
REQUIRE(d);
REQUIRE(c == 2.4);
REQUIRE(b == 10);
REQUIRE(a == 5);
}