overload

calling different functions based on argument number/type

function: create overloaded set
1
2
3
4
5
template <typename... Args>
struct overloaded_set : std::tuple<Args...> { /* ... */ };

template <typename... Args>
overloaded_set<Args...> overload( Args&&... args );

The actual class produced by sol::overload is essentially a type-wrapper around std::tuple that signals to the library that an overload is being created. The function helps users make overloaded functions that can be called from Lua using 1 name but multiple arguments. It is meant to replace the spaghetti of code where users mock this up by doing strange if statements and switches on what version of a function to call based on luaL_check{number/udata/string}.

Note

Please note that default parameters in a function (e.g., int func(int a = 20)) do not exist beyond C++’s compile-time fun. When that function gets bound or serialized into Lua’s framework, it is bound as a function taking 1 argument, not 2 functions taking either 0 or 1 argument. If you want to achieve the same effect, then you need to use overloading and explicitly call the version of the function you want. There is no magic in C++ that allows me to retrieve default parameters and set this up automatically.

Note

Overload resolution can be affected by configuration defines in the safety pages. For example, it is impossible to differentiate between integers (uint8_t, in32_t, etc.) versus floating-point types (float, double, half) when SOL_SAFE_NUMERICS is not turned on.

Its use is simple: wherever you can pass a function type to Lua, whether its on a usertype or if you are just setting any kind of function with set or set_function (for table or state(_view)), simply wrap up the functions you wish to be considered for overload resolution on one function like so:

sol::overload( func1, func2, ... funcN );

The functions can be any kind of function / function object (lambda). Given these functions and struct:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>

#include "assert.hpp"

#include <iostream>

struct pup {
	int barks = 0;

	void bark () {
		++barks; // bark!
	}

	bool is_cute () const { 
		return true;
	}
};

void ultra_bark( pup& p, int barks) {
	for (; barks --> 0;) p.bark();
}

void picky_bark( pup& p, std::string s) {
	if ( s == "bark" )
	    p.bark();
}

You then use it just like you would for any other part of the api:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int main () {
	std::cout << "=== overloading with members ===" << std::endl;

	sol::state lua;
	lua.open_libraries(sol::lib::base);

	lua.set_function( "bark", sol::overload( 
		ultra_bark, 
		[]() { return "the bark from nowhere"; } 
	) );

	lua.new_usertype<pup>( "pup",
		// regular function
		"is_cute", &pup::is_cute,
		// overloaded function
		"bark", sol::overload( &pup::bark, &picky_bark )
	);

Doing the following in Lua will call the specific overloads chosen, and their associated functions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
	const auto& code = R"(
	barker = pup.new()
	print(barker:is_cute())
	barker:bark() -- calls member function pup::bark
	barker:bark("meow") -- picky_bark, no bark
	barker:bark("bark") -- picky_bark, bark

	bark(barker, 20) -- calls ultra_bark
	print(bark()) -- calls lambda which returns that string
	)";

	lua.script(code);

	pup& barker = lua["barker"];
	std::cout << barker.barks << std::endl;
	c_assert(barker.barks == 22);

	std::cout << std::endl;
	return 0;
}

Note

Overloading is done on a first-come, first-serve system. This means if two overloads are compatible, workable overloads, it will choose the first one in the list.

Note that because of this system, you can use sol::variadic_args to make a function that serves as a “fallback”. Be sure that it is the last specified function in the listed functions for sol::overload( ... ). This example shows how.

Note

Please keep in mind that doing this bears a runtime cost to find the proper overload. The cost scales directly not exactly with the number of overloads, but the number of functions that have the same argument count as each other (sol will early-eliminate any functions that do not match the argument count).