]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright (c) 2018-2020 Emil Dotchevski and Reverge Studios, Inc. |
2 | ||
3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
6 | // This is a simple program that shows how to report error objects out of a | |
7 | // C-callback, converting them to leaf::result<T> as soon as controlreaches C++. | |
8 | ||
9 | extern "C" { | |
10 | #include "lua.h" | |
11 | #include "lauxlib.h" | |
12 | } | |
13 | #include <boost/leaf/handle_errors.hpp> | |
14 | #include <boost/leaf/result.hpp> | |
15 | #include <boost/leaf/on_error.hpp> | |
16 | #include <iostream> | |
17 | #include <stdlib.h> | |
18 | ||
19 | namespace leaf = boost::leaf; | |
20 | ||
21 | enum do_work_error_code | |
22 | { | |
23 | ec1=1, | |
24 | ec2 | |
25 | }; | |
26 | ||
27 | struct e_lua_pcall_error { int value; }; | |
28 | struct e_lua_error_message { std::string value; }; | |
29 | ||
30 | ||
31 | // This is a C callback with a specific signature, callable from programs written in Lua. | |
32 | // If it succeeds, it returns an int answer, by pushing it onto the Lua stack. But "sometimes" | |
33 | // it fails, in which case it calls luaL_error. This causes the Lua interpreter to abort and | |
34 | // pop back into the C++ code which called it (see call_lua below). | |
35 | int do_work( lua_State * L ) | |
36 | { | |
37 | bool success = rand()%2; // "Sometimes" do_work fails. | |
38 | if( success ) | |
39 | { | |
40 | lua_pushnumber(L, 42); // Success, push the result on the Lua stack, return to Lua. | |
41 | return 1; | |
42 | } | |
43 | else | |
44 | { | |
45 | return leaf::new_error(ec1), luaL_error(L,"do_work_error"); // luaL_error does not return (longjmp). | |
46 | } | |
47 | } | |
48 | ||
49 | ||
50 | std::shared_ptr<lua_State> init_lua_state() | |
51 | { | |
52 | // Create a new lua_State, we'll use std::shared_ptr for automatic cleanup. | |
53 | std::shared_ptr<lua_State> L(lua_open(), &lua_close); | |
54 | ||
55 | // Register the do_work function (above) as a C callback, under the global | |
56 | // Lua name "do_work". With this, calls from Lua programs to do_work | |
57 | // will land in the do_work C function we've registered. | |
58 | lua_register( &*L, "do_work", &do_work ); | |
59 | ||
60 | // Pass some Lua code as a C string literal to Lua. This creates a global Lua | |
61 | // function called "call_do_work", which we will later ask Lua to execute. | |
62 | luaL_dostring( &*L, "\ | |
63 | \n function call_do_work()\ | |
64 | \n return do_work()\ | |
65 | \n end" ); | |
66 | ||
67 | return L; | |
68 | } | |
69 | ||
70 | ||
71 | // Here we will ask Lua to execute the function call_do_work, which is written | |
72 | // in Lua, and returns the value from do_work, which is written in C++ and | |
73 | // registered with the Lua interpreter as a C callback. | |
74 | ||
75 | // If do_work succeeds, we return the resulting int answer. | |
76 | // If it fails, we'll communicate that failure to our caller. | |
77 | leaf::result<int> call_lua( lua_State * L ) | |
78 | { | |
79 | leaf::error_monitor cur_err; | |
80 | ||
81 | // Ask the Lua interpreter to call the global Lua function call_do_work. | |
82 | lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" ); | |
83 | if( int err = lua_pcall(L, 0, 1, 0) ) // Ask Lua to call the global function call_do_work. | |
84 | { | |
85 | auto load = leaf::on_error(e_lua_error_message{lua_tostring(L, 1)}); | |
86 | lua_pop(L,1); | |
87 | ||
88 | // We got a Lua error which may be the error we're reporting from do_work, or some other error. | |
89 | // If it is another error, cur_err.assigned_error_id() will return a new leaf::error_id, | |
90 | // otherwise we'll be working with the original value returned by leaf::new_error in do_work. | |
91 | return cur_err.assigned_error_id().load(e_lua_pcall_error{err}); | |
92 | } | |
93 | else | |
94 | { | |
95 | // Success! Just return the int answer. | |
96 | int answer=lua_tonumber(L, -1); | |
97 | lua_pop(L,1); | |
98 | return answer; | |
99 | } | |
100 | } | |
101 | ||
102 | int main() | |
103 | { | |
104 | std::shared_ptr<lua_State> L=init_lua_state(); | |
105 | ||
106 | for( int i=0; i!=10; ++i ) | |
107 | { | |
108 | leaf::try_handle_all( | |
109 | ||
110 | [&]() -> leaf::result<void> | |
111 | { | |
112 | BOOST_LEAF_AUTO(answer, call_lua(&*L)); | |
113 | std::cout << "do_work succeeded, answer=" << answer << '\n'; | |
114 | return { }; | |
115 | }, | |
116 | ||
117 | []( do_work_error_code e ) | |
118 | { | |
119 | std::cout << "Got do_work_error_code = " << e << "!\n"; | |
120 | }, | |
121 | ||
122 | []( e_lua_pcall_error const & err, e_lua_error_message const & msg ) | |
123 | { | |
124 | std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n"; | |
125 | }, | |
126 | ||
127 | []( leaf::error_info const & unmatched ) | |
128 | { | |
129 | std::cerr << | |
130 | "Unknown failure detected" << std::endl << | |
131 | "Cryptic diagnostic information follows" << std::endl << | |
132 | unmatched; | |
133 | } ); | |
134 | } | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | #ifdef BOOST_LEAF_NO_EXCEPTIONS | |
140 | ||
141 | namespace boost | |
142 | { | |
143 | BOOST_LEAF_NORETURN void throw_exception( std::exception const & e ) | |
144 | { | |
145 | std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what(); | |
146 | std::terminate(); | |
147 | } | |
148 | ||
149 | struct source_location; | |
150 | BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) | |
151 | { | |
152 | throw_exception(e); | |
153 | } | |
154 | } | |
155 | ||
156 | #endif |