summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/sol2/docs/source/tutorial/customization.rst
blob: 5479c804115f8bf37ef1238ae16c0c17c9da6660 (plain) (blame)
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
adding your own types
=====================

Sometimes, overriding Sol to make it handle certain ``struct``'s and ``class``'es as something other than just userdata is desirable. The way to do this is to take advantage of the 4 customization points for Sol. These are ``sol::lua_size<T>``, ``sol::stack::pusher<T, C>``, ``sol::stack::getter<T, C>``, ``sol::stack::checker<T, sol::type t,  C>``.

These are template class/structs, so you'll override them using a technique C++ calls *class/struct specialization*. Below is an example of a struct that gets broken apart into 2 pieces when going in the C++ --> Lua direction, and then pulled back into a struct when going in the Lua --> C++:

.. code-block:: cpp
	:caption: two_things.hpp
	:name: customization-overriding

	#include <sol.hpp>

	struct two_things {
		int a;
		bool b;
	};

	namespace sol {

		// First, the expected size
		// Specialization of a struct
		// We expect 2, so use 2
		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) {
					// indices can be negative to count backwards from the top of the stack,
					// rather than the bottom up
					// to deal with this, we adjust the index to
					// its absolute position using the lua_absindex function 
					int absolute_index = lua_absindex(L, index);
					// Check first and second second index for being the proper types
					bool success = stack::check<int>(L, absolute_index - 1, handler) 
						&& stack::check<bool>(L, absolute_index, handler);
					tracking.use(2);
					return success;
				}
			};

			template <>
			struct getter<two_things> {
				static two_things get(lua_State* L, int index, record& tracking) {
					int absolute_index = lua_absindex(L, index);
					// Get the first element
					int a = stack::get<int>(L, absolute_index - 1);
					// Get the second element, 
					// in the +1 position from the first
					bool b = stack::get<bool>(L, absolute_index);
					// 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 will be 1: int pushes 1 item
					amount += stack::push(L, things.b);
					// amount 2 now, since bool pushes a single item
					// Return 2 things
					return amount;
				}
			};

		}
	}


This is the base formula that you can follow to extend to your own classes. Using it in the rest of the library should then be seamless:

.. code-block:: cpp
	:caption: customization: using it
	:name: customization-using

	#include <sol.hpp>
	#include <two_things.hpp>

	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
				
		return 0;
	}


And that's it!

A few things of note about the implementation: First, there's an auxiliary parameter of type :doc:`sol::stack::record<../api/stack>` for the getters and checkers. This keeps track of what the last complete operation performed. Since we retrieved 2 members, we use ``tracking.use(2);`` to indicate that we used 2 stack positions (one for ``bool``, one for ``int``). The second thing to note here is that we made sure to use the ``index`` parameter, and then proceeded to add 1 to it for the next one.

You can make something pushable into Lua, but not get-able in the same way if you only specialize one part of the system. If you need to retrieve it (as a return using one or multiple values from Lua), you should specialize the ``sol::stack::getter`` template class and the ``sol::stack::checker`` template class. If you need to push it into Lua at some point, then you'll want to specialize the ``sol::stack::pusher`` template class. The ``sol::lua_size`` template class trait needs to be specialized for both cases, unless it only pushes 1 item, in which case the default implementation will assume 1.

.. note::

	It is important to note here that the ``getter``, ``pusher`` and ``checker`` differentiate between a type ``T`` and a pointer to a type ``T*``. This means that if you want to work purely with, say, a ``T*`` handle that does not have the same semantics as just ``T``, you may need to specify checkers/getters/pushers for both ``T*`` and ``T``. The checkers for ``T*`` forward to the checkers for ``T``, but the getter for ``T*`` does not forward to the getter for ``T`` (e.g., because of ``int*`` not being quite the same as ``int``).

In general, this is fine since most getters/checkers only use 1 stack point. But, if you're doing more complex nested classes, it would be useful to use ``tracking.last`` to understand how many stack indices the last getter/checker operation did and increment it by ``index + tracking.last`` after using a ``stack::check<..>( L, index, tracking)`` call.

You can read more about the structs themselves :ref:`over on the API page for stack<extension_points>`, and if there's something that goes wrong or you have anymore questions, please feel free to drop a line on the Github Issues page or send an e-mail!
s="p">{ samples += pmd85_emit_level (buffer, sample_pos + samples, PMD85_BIT_LENGTH/2, WAVEENTRY_LOW); samples += pmd85_emit_level (buffer, sample_pos + samples, PMD85_BIT_LENGTH/2, WAVEENTRY_HIGH); } else { samples += pmd85_emit_level (buffer, sample_pos + samples, PMD85_BIT_LENGTH/2, WAVEENTRY_HIGH); samples += pmd85_emit_level (buffer, sample_pos + samples, PMD85_BIT_LENGTH/2, WAVEENTRY_LOW); } return samples; } static int pmd85_output_byte(INT16 *buffer, int sample_pos, UINT8 byte) { int samples = 0; /* start */ samples += pmd85_output_bit (buffer, sample_pos + samples, 0); /* data */ for (int i=0; i<8; i++) samples += pmd85_output_bit(buffer,sample_pos + samples, (byte>>i) & 0x01); /* stop */ samples += pmd85_output_bit (buffer, sample_pos + samples, 1); samples += pmd85_output_bit (buffer, sample_pos + samples, 1); return samples; } static bool pmd85_is_header_block(const UINT8 *bytes) { for (int i=0; i<0x10; i++) { if (bytes[i] != 0xff || bytes[i + 0x10] != 0x00 || bytes[i + 0x20] != 0x55) return false; } return true; } static void pmd85_printf_image_info(const UINT8 *bytes, int sample_count) { #if 0 char track_name[9]; UINT32 sec = (UINT32)(sample_count/PMD85_WAV_FREQUENCY); UINT16 addr = (bytes[0x33]<<8) | bytes[0x32]; strncpy(track_name, (char*)&bytes[0x36], 8); track_name[8] = '\0'; printf("Block ID: %02d %s 0x%04x Tape pos: %02d:%02d\n", bytes[0x30], track_name, addr, sec/60, sec%60); #endif } static int pmd85_handle_cassette(INT16 *buffer, const UINT8 *bytes) { int sample_count = 0; if (pmd85_is_header_block(bytes)) { // PMD file /* pilot */ for (int i=0; i<PMD85_PILOT_BITS; i++) sample_count += pmd85_output_bit(buffer, sample_count, 1); if (!buffer) pmd85_printf_image_info(bytes, sample_count); /* header */ for (int i=0; i<PMD85_HEADER_BYTES; i++) sample_count += pmd85_output_byte(buffer, sample_count, bytes[i]); /* pause */ for (int i=0; i<PMD85_PAUSE_BITS; i++) sample_count += pmd85_output_bit(buffer, sample_count, 1); /* data */ for (int i=PMD85_HEADER_BYTES; i<pmd85_image_size; i++) sample_count += pmd85_output_byte(buffer, sample_count, bytes[i]); } else { // PTP file /* pilot */ for (int i=0; i<PMD85_PILOT_BITS; i++) sample_count += pmd85_output_bit(buffer, sample_count, 1); int data_pos = 0; while (data_pos < pmd85_image_size) { UINT16 block_size = (bytes[data_pos + 1]<<8) | bytes[data_pos]; int pause_len = PMD85_PAUSE_BITS; data_pos += 2; if (pmd85_is_header_block(bytes + data_pos)) { if (!buffer) pmd85_printf_image_info(bytes + data_pos, sample_count); pause_len *= 2; } for (int i=0; i<pause_len; i++) sample_count += pmd85_output_bit(buffer, sample_count, 1); for (int i=0; i<block_size; i++) sample_count += pmd85_output_byte(buffer, sample_count, bytes[data_pos + i]); data_pos += block_size; } } return sample_count; } /******************************************************************* Generate samples for the tape image ********************************************************************/ static int pmd85_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) { return pmd85_handle_cassette(buffer, bytes); } /******************************************************************* Calculate the number of samples needed for this tape image ********************************************************************/ static int pmd85_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) { pmd85_image_size = length; return pmd85_handle_cassette(NULL, bytes); } static const struct CassetteLegacyWaveFiller pmd85_legacy_fill_wave = { pmd85_cassette_fill_wave, /* fill_wave */ -1, /* chunk_size */ 0, /* chunk_samples */ pmd85_cassette_calculate_size_in_samples, /* chunk_sample_calc */ PMD85_WAV_FREQUENCY, /* sample_frequency */ 0, /* header_samples */ 0 /* trailer_samples */ }; static casserr_t pmd85_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts) { return cassette_legacy_identify(cassette, opts, &pmd85_legacy_fill_wave); } static casserr_t pmd85_cassette_load(cassette_image *cassette) { return cassette_legacy_construct(cassette, &pmd85_legacy_fill_wave); } static const struct CassetteFormat pmd85_cassette_image_format = { "pmd,tap,ptp", pmd85_cassette_identify, pmd85_cassette_load, NULL }; CASSETTE_FORMATLIST_START(pmd85_cassette_formats) CASSETTE_FORMAT(pmd85_cassette_image_format) CASSETTE_FORMATLIST_END