2
0
mirror of https://github.com/boostorg/leaf.git synced 2026-01-19 04:22:08 +00:00

tweaks, pretty much complete documentation

This commit is contained in:
Emil Dotchevski
2018-10-27 17:38:33 -07:00
parent 34a7717f8b
commit 28961367a3
15 changed files with 1267 additions and 369 deletions

3
.gitignore vendored
View File

@@ -7,4 +7,5 @@
*.opensdf
*.db
*.opendb
bld/*
bld/*
subprojects/*/

File diff suppressed because it is too large Load Diff

133
example/lua_callback_eh.cpp Normal file
View File

@@ -0,0 +1,133 @@
//Copyright (c) 2018 Emil Dotchevski
//Copyright (c) 2018 Second Spectrum, 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 propagate leaf::error objects out
//of a C-callback, and converting them to leaf::result<T> as soon as control
//reaches C++.
extern "C" {
#include "lua.h"
#include "lauxlib.h"
}
#include <boost/leaf/all.hpp>
#include <iostream>
#include <stdlib.h>
namespace leaf = boost::leaf;
struct lua_failure: std::exception { };
struct e_do_work_error { int value; };
struct e_lua_pcall_error { int value; };
struct e_lua_error_message { std::string value; };
//This is a C callback function with a specific signature, made accessible to 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 ) noexcept
{
bool success=rand()%2;
if( success )
{
lua_pushnumber(L,42); //Return 42 to the calling Lua program.
return 1;
}
else
{
//Tell the Lua interpreter to abort the Lua program. Control will reach the
//call_lua function which called the Lua interpreter. The e_do_work_error
//is communicated, through the Lua interpreter, to that function.
leaf::preload( e_do_work_error{-42} );
return luaL_error(L,"do_work_error");
}
}
std::shared_ptr<lua_State> init_lua_state() noexcept
{
//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.
int call_lua( lua_State * L )
{
//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) )
{
//Something went wrong with the call, so we'll throw lua_failure.
//If this is a do_work failure, the e_do_work object preloaded in
//do_work will become associated with this exception. If not,
//we will still need to communicate that the lua_pcall failed with an
//error code and an error message.
leaf::preload( e_lua_error_message{lua_tostring(L,1)} );
lua_pop(L,1);
leaf::throw_exception( lua_failure(), e_lua_pcall_error{err} );
}
else
{
//Success! Just return the int answer.
int answer=lua_tonumber(L,-1);
lua_pop(L,1);
return answer;
}
}
int main() noexcept
{
std::shared_ptr<lua_State> L=init_lua_state();
leaf::expect<e_do_work_error,e_lua_pcall_error,e_lua_error_message> exp;
for( int i=0; i!=10; ++i )
try
{
int r = call_lua(&*L);
std::cout << "do_work succeeded, answer=" << r << '\n';
}
catch( lua_failure const & e )
{
handle_exception( exp, e,
//Handle e_do_work failures:
leaf::match<e_do_work_error>( [ ]( int v )
{
std::cout << "Got e_do_work_error, value = " << v << "!\n";
} ),
//Handle all other lua_pcall failures:
leaf::match<e_lua_pcall_error,e_lua_error_message>( [ ]( int err, std::string const & msg )
{
std::cout << "Got e_lua_pcall_error, Lua error code = " << err << ", " << msg << "\n";
} )
);
}
return 0;
}

View File

@@ -0,0 +1,129 @@
//Copyright (c) 2018 Emil Dotchevski
//Copyright (c) 2018 Second Spectrum, 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 propagate leaf::error objects out
//of a C-callback, and converting them to leaf::result<T> as soon as control
//reaches C++.
extern "C" {
#include "lua.h"
#include "lauxlib.h"
}
#include <boost/leaf/all.hpp>
#include <iostream>
#include <stdlib.h>
namespace leaf = boost::leaf;
struct e_do_work_error { int value; };
struct e_lua_pcall_error { int value; };
struct e_lua_error_message { std::string value; };
//This is a C callback function with a specific signature, made accessible to 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 ) noexcept
{
bool success=rand()%2;
if( success )
{
lua_pushnumber(L,42); //Return 42 to the calling Lua program.
return 1;
}
else
{
//Tell the Lua interpreter to abort the Lua program. Control will reach the
//call_lua function which called the Lua interpreter. The e_do_work_error
//is communicated, through the Lua interpreter, to that function.
leaf::preload( e_do_work_error{-42} );
return luaL_error(L,"do_work_error");
}
}
std::shared_ptr<lua_State> init_lua_state() noexcept
{
//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 in leaf::result<int>.
//If it fails, we'll communicate that failure to our caller.
leaf::result<int> call_lua( lua_State * L )
{
//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) )
{
//Something went wrong with the call, so we'll return a leaf::error.
//If this is a do_work failure, the e_do_work object preloaded in
//do_work will become associated with this leaf::error value. If not,
//we will still need to communicate that the lua_pcall failed with an
//error code and an error message.
leaf::preload( e_lua_error_message{lua_tostring(L,1)} );
lua_pop(L,1);
return leaf::error( e_lua_pcall_error{err} );
}
else
{
//Success! Just return the int answer.
int answer=lua_tonumber(L,-1);
lua_pop(L,1);
return answer;
}
}
int main() noexcept
{
std::shared_ptr<lua_State> L=init_lua_state();
leaf::expect<e_do_work_error,e_lua_pcall_error,e_lua_error_message> exp;
for( int i=0; i!=10; ++i )
if( leaf::result<int> r = call_lua(&*L) )
std::cout << "do_work succeeded, answer=" << *r << '\n';
else
{
bool matched = handle_error( exp, r,
//Handle e_do_work failures:
leaf::match<e_do_work_error>( [ ]( int v )
{
std::cout << "Got e_do_work_error, value = " << v << "!\n";
} ),
//Handle all other lua_pcall failures:
leaf::match<e_lua_pcall_error,e_lua_error_message>( [ ]( int err, std::string const & msg )
{
std::cout << "Got e_lua_pcall_error, Lua error code = " << err << ", " << msg << "\n";
} )
);
assert(matched);
}
return 0;
}

View File

@@ -46,7 +46,7 @@ std::shared_ptr<FILE> file_open( char const * file_name )
int file_size( FILE & f )
{
//All exceptions escaping this function will automatically propagate errno.
auto propagate = leaf::defer(&leaf::get_errno);
auto propagate = leaf::defer([ ] { return e_errno{errno}; } );
if( fseek(&f,0,SEEK_END) )
throw input_file_size_error();

View File

@@ -45,7 +45,7 @@ leaf::result<std::shared_ptr<FILE>> file_open( char const * file_name )
leaf::result<int> file_size( FILE & f )
{
auto propagate = leaf::defer(&leaf::get_errno);
auto propagate = leaf::defer([ ] { return e_errno{errno}; } );
if( fseek(&f,0,SEEK_END) )
return leaf::error( e_error_code{input_file_size_error} );

View File

@@ -57,22 +57,13 @@ boost
{
int value;
friend
inline
std::ostream &
operator<<( std::ostream & os, e_errno const & err )
{
using namespace std;
os << type<e_errno>() << " = " << err.value << ", \"" << std::strerror(err.value) << '"';
return os;
return os << type<e_errno>() << " = " << err.value << ", \"" << std::strerror(err.value) << '"';
}
};
inline
e_errno
get_errno() noexcept
{
using namespace std;
return e_errno{errno};
}
}
}

View File

@@ -0,0 +1,57 @@
//Copyright (c) 2018 Emil Dotchevski
//Copyright (c) 2018 Second Spectrum, 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)
#ifndef UUID_75F38740D98D11E881DDB244C82C3C47
#define UUID_75F38740D98D11E881DDB244C82C3C47
#include <boost/leaf/common.hpp>
#include <exception>
#define LEAF_THROW(e) ::boost::leaf::throw_exception(e,LEAF_SOURCE_LOCATION)
namespace
boost
{
namespace
leaf
{
namespace
leaf_detail
{
inline void enforce_std_exception( std::exception const & ) { }
template <class Ex>
class
exception:
public Ex,
public error
{
public:
exception( Ex && ex, error && e ) noexcept:
Ex(std::move(ex)),
error(std::move(e))
{
enforce_std_exception(*this);
}
};
}
template <class... E,class Ex>
[[noreturn]]
void
throw_exception( Ex && ex, E && ... e )
{
throw leaf_detail::exception<Ex>(std::move(ex),error(std::move(e)...));
}
template <class... E,class Ex>
[[noreturn]]
void
throw_exception( Ex && ex, error const & err, E && ... e )
{
throw leaf_detail::exception<Ex>(std::move(ex),err.propagate(std::move(e)...));
}
}
}
#endif

View File

@@ -227,7 +227,7 @@ boost
}
template <class... E>
void
preload( E && ... e )
preload( E && ... e ) noexcept
{
error::peek_next_error().propagate(std::forward<E>(e)...);
}

View File

@@ -91,23 +91,23 @@ boost
////////////////////////////////////////
template <int I,class Tuple>
struct
tuple_propagate
tuple_unload
{
static
void
propagate( error const & e, Tuple && tup ) noexcept
unload( error const & e, Tuple && tup ) noexcept
{
tuple_propagate<I-1,Tuple>::propagate(e,std::move(tup));
tuple_unload<I-1,Tuple>::unload(e,std::move(tup));
auto && opt = std::get<I-1>(std::move(tup));
if( opt.has_value() )
e.propagate(std::move(opt).value());
(void) e.propagate(std::move(opt).value());
}
};
template <class Tuple>
struct
tuple_propagate<0,Tuple>
tuple_unload<0,Tuple>
{
static void propagate( error const &, Tuple && ) noexcept { }
static void unload( error const &, Tuple && ) noexcept { }
};
}
////////////////////////////////////////
@@ -153,7 +153,7 @@ boost
return 0;
}
virtual void diagnostic_print( std::ostream & ) const = 0;
virtual void propagate( error const & ) noexcept = 0;
virtual void unload( error const & ) noexcept = 0;
};
template <class... T>
class
@@ -180,9 +180,9 @@ boost
leaf_detail::tuple_print<sizeof...(T),decltype(s_)>::print(os,s_);
}
void
propagate( error const & e ) noexcept
unload( error const & e ) noexcept
{
leaf_detail::tuple_propagate<sizeof...(T),decltype(s_)>::propagate(e,std::move(s_));
leaf_detail::tuple_unload<sizeof...(T),decltype(s_)>::unload(e,std::move(s_));
}
};
void
@@ -269,11 +269,11 @@ boost
return s_!=0;
}
error
propagate() noexcept
unload() noexcept
{
if( s_ )
{
s_->propagate(e_);
s_->unload(e_);
free();
}
return e_;

View File

@@ -8,9 +8,7 @@
#define UUID_87F274C4D4BA11E89928D55AC82C3C47
#include <boost/leaf/expect.hpp>
#include <exception>
#define LEAF_THROW(e) ::boost::leaf::throw_exception(e,LEAF_SOURCE_LOCATION)
#include <boost/leaf/detail/throw_exception.hpp>
namespace
boost
@@ -18,40 +16,6 @@ boost
namespace
leaf
{
namespace
leaf_detail
{
inline void enforce_std_exception( std::exception const & ) { }
template <class Ex>
class
exception:
public Ex,
public error
{
public:
exception( Ex && ex, error && e ) noexcept:
Ex(std::move(ex)),
error(std::move(e))
{
enforce_std_exception(*this);
}
};
}
template <class... E,class Ex>
[[noreturn]]
void
throw_exception( Ex && ex, E && ... e )
{
throw leaf_detail::exception<Ex>(std::move(ex),error(std::move(e)...));
}
template <class... E,class Ex>
[[noreturn]]
void
throw_exception( Ex && ex, error const & err, E && ... e )
{
throw leaf_detail::exception<Ex>(std::move(ex),err.propagate(std::move(e)...));
}
////////////////////////////////////////
template <class P,class... E>
decltype(P::value) const *
peek( expect<E...> const & exp, std::exception const & e ) noexcept

View File

@@ -44,7 +44,7 @@ boost
set_error(error::peek_next_error());
has_error_ = true;
}
propagate();
unload();
std::rethrow_exception(ex_);
}
friend

View File

@@ -8,6 +8,7 @@
#define UUID_2CD8E6B8CA8D11E8BD3B80D66CE5B91B
#include <boost/leaf/error_capture.hpp>
#include <boost/leaf/detail/throw_exception.hpp>
#define LEAF_AUTO(v,r) auto _r_##v = r; if( !_r_##v ) return _r_##v.error(); auto & v = *_r_##v
#define LEAF_CHECK(r) {auto _r_##v = r; if( !_r_##v ) return _r_##v.error();}
@@ -166,41 +167,6 @@ boost
move_from(std::move(x));
return *this;
}
void
reset( T const & v )
{
destroy();
(void) new(&value_) T(v);
which_ = variant::value;
}
void
reset( T && v ) noexcept
{
destroy();
(void) new(&value_) T(std::move(v));
which_ = variant::value;
}
void
reset( leaf::error const & e ) noexcept
{
destroy();
(void) new(&err_) leaf::error(e);
which_ = variant::err;
}
void
reset( leaf::error_capture const & cap ) noexcept
{
destroy();
(void) new(&cap_) leaf::error_capture(cap);
which_ = variant::cap;
}
void
reset( leaf::error_capture && cap ) noexcept
{
destroy();
(void) new(&cap_) leaf::error_capture(std::move(cap));
which_ = variant::cap;
}
explicit
operator bool() const noexcept
{
@@ -212,7 +178,7 @@ boost
if( which_==variant::value )
return value_;
else
throw bad_result();
LEAF_THROW(bad_result());
}
T &
value()
@@ -220,7 +186,7 @@ boost
if( which_==variant::value )
return value_;
else
throw bad_result();
LEAF_THROW(bad_result());
}
T const &
operator*() const
@@ -243,7 +209,9 @@ boost
return leaf::error(std::forward<E>(e)...);
case variant::
cap:
reset(cap_.propagate());
destroy();
(void) new(&err_) leaf::error(cap_.unload());
which_ = variant::err;
default:
assert(which_==variant::err);
return err_.propagate(std::forward<E>(e)...);
@@ -251,17 +219,13 @@ boost
}
template <class... E>
friend
result &&
capture( expect<E...> & exp, result && r )
result
capture( expect<E...> & exp, result const & r )
{
if( r.which_==variant::err )
{
auto cap = capture(exp,r.err_);
r.err_.~error();
(void) new (&r.cap_) error_capture(std::move(cap));
r.which_ = variant::cap;
}
return std::move(r);
return capture(exp,r.err_);
else
return r;
}
template <class... M,class... E>
friend
@@ -303,6 +267,11 @@ boost
typedef result<bool> base;
result( result<bool> && rb ):
base(std::move(rb))
{
}
public:
~result() noexcept
@@ -325,17 +294,12 @@ boost
}
using base::operator bool;
using base::error;
using base::reset;
void reset( bool const & ) = delete;
void reset( bool && ) = delete;
template <class... E>
friend
result &&
capture( expect<E...> & exp, result && r )
result
capture( expect<E...> & exp, result const & r )
{
result<bool> && rb = std::move(r);
(void) capture(exp,std::move(rb));
return std::move(r);
return capture(exp,static_cast<result<bool> const &>(r));
}
template <class... M,class... E>
friend

View File

@@ -18,6 +18,8 @@ if not meson.is_subproject()
endif
endif
lua=subproject('lua').get_variable('all')
includes = [ include_directories('include') ]
leaf = declare_dependency( include_directories: includes )
@@ -58,3 +60,6 @@ examples = [
foreach e : examples
executable(e, 'example/'+e+'.cpp', dependencies: [ leaf ] )
endforeach
executable('lua_callback_result', 'example/lua_callback_result.cpp', dependencies: [ leaf, lua ] )
executable('lua_callback_eh', 'example/lua_callback_eh.cpp', dependencies: [ leaf, lua ] )

10
subprojects/lua.wrap Normal file
View File

@@ -0,0 +1,10 @@
[wrap-file]
directory=lua-5.1.5
source_url = http://www.lua.org/ftp/lua-5.1.5.tar.gz
source_filename = lua-5.1.5.tar.gz
source_hash = 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
patch_url = https://github.com/zajo/meson_wraps/blob/master/lua/subprojects/lua.tar.gz?raw=true
patch_filename = lua.tar.gz
patch_hash = 51cd288c6ce32cd338e39cf9a1343dcb2ea9f05293055c96dcce87781d3b0afb