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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// This is a simple program that shows how to report error objects out of a
// C-callback, converting them to leaf::result<T> as soon as controlreaches C++.
extern "C" {
#include "lua.h"
#include "lauxlib.h"
}
#include <boost/leaf.hpp>
#include <iostream>
#include <stdlib.h>
namespace leaf = boost::leaf;
enum do_work_error_code
{
ec1=1,
ec2
};
struct e_lua_pcall_error
{
int value;
friend std::ostream & operator<<( std::ostream & os, e_lua_pcall_error const & x )
{
os << "Lua error code = " << x.value;
switch( x.value )
{
case LUA_ERRRUN: return os << " (LUA_ERRRUN)";
case LUA_ERRMEM: return os << " (LUA_ERRMEM)";
case LUA_ERRERR: return os << " (LUA_ERRERR)";
default: return os << " (unknown)";
}
}
};
struct e_lua_error_message { std::string value; };
// This is a C callback with a specific signature, callable from programs
// written in Lua. If it succeeds, it returns an int answer, by pushing it onto
// the Lua stack. But "sometimes" it fails, in which case it calls luaL_error.
// This causes the Lua interpreter to abort and pop back into the C++ code which
// called it (see call_lua below).
int do_work( lua_State * L )
{
bool success = rand() % 2; // "Sometimes" do_work fails.
if( success )
{
lua_pushnumber(L, 42); // Success, push the result on the Lua stack, return to Lua.
return 1;
}
else
{
return leaf::new_error(ec1), luaL_error(L,"do_work_error"); // luaL_error does not return (longjmp).
}
}
std::shared_ptr<lua_State> init_lua_state()
{
// Create a new lua_State, we'll use std::shared_ptr for automatic cleanup.
std::shared_ptr<lua_State> L(lua_open(), &lua_close);
// Register the do_work function (above) as a C callback, under the global
// Lua name "do_work". With this, calls from Lua programs to do_work will
// land in the do_work C function we've registered.
lua_register( &*L, "do_work", &do_work );
// Pass some Lua code as a C string literal to Lua. This creates a global
// Lua function called "call_do_work", which we will later ask Lua to
// execute.
luaL_dostring( &*L, "\
\n function call_do_work()\
\n return do_work()\
\n end" );
return L;
}
// Here we will ask Lua to execute the function call_do_work, which is written
// in Lua, and returns the value from do_work, which is written in C++ and
// registered with the Lua interpreter as a C callback.
// If do_work succeeds, we return the resulting int answer. If it fails, we'll
// communicate that failure to our caller.
leaf::result<int> call_lua( lua_State * L )
{
leaf::error_monitor cur_err;
// Ask the Lua interpreter to call the global Lua function call_do_work.
lua_getfield( L, LUA_GLOBALSINDEX, "call_do_work" );
if( int err = lua_pcall(L, 0, 1, 0) ) // Ask Lua to call the global function call_do_work.
{
std::string msg = lua_tostring(L, 1);
lua_pop(L, 1);
// We got a Lua error which may be the error we're reporting from
// do_work, or some other error. If it is another error,
// cur_err.assigned_error_id() will return a new leaf::error_id,
// otherwise we'll be working with the original value returned by
// leaf::new_error in do_work.
return cur_err.assigned_error_id().load( e_lua_pcall_error{err}, e_lua_error_message{std::move(msg)} );
}
else
{
// Success! Just return the int answer.
int answer = lua_tonumber(L, -1);
lua_pop(L, 1);
return answer;
}
}
int main()
{
std::shared_ptr<lua_State> L=init_lua_state();
for( int i=0; i!=10; ++i )
{
leaf::try_handle_all(
[&]() -> leaf::result<void>
{
BOOST_LEAF_AUTO(answer, call_lua(&*L));
std::cout << "do_work succeeded, answer=" << answer << '\n';
return { };
},
[]( do_work_error_code e, e_lua_error_message const & msg )
{
std::cout << "Got do_work_error_code = " << e << ", " << msg.value << "\n";
},
[]( e_lua_pcall_error const & err, e_lua_error_message const & msg )
{
std::cout << "Got e_lua_pcall_error, " << err << ", " << msg.value << "\n";
},
[]( leaf::error_info const & unmatched )
{
std::cerr <<
"Unknown failure detected" << std::endl <<
"Cryptic diagnostic information follows" << std::endl <<
unmatched;
} );
}
return 0;
}
#ifdef BOOST_LEAF_NO_EXCEPTIONS
namespace boost
{
[[noreturn]] void throw_exception( std::exception const & e )
{
std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what();
std::terminate();
}
struct source_location;
[[noreturn]] void throw_exception( std::exception const & e, boost::source_location const & )
{
throw_exception(e);
}
}
#endif
|