[lclcontext] Any interest in Pythonesque "with-as" statement in C++.

I think Python's concept of Context and Context Managers useful and I've adapted, to the best of my abilities, a similar concept in C++. For those not familiar with this concept, I'll quote a brief snippet from the documentation: "Simply stated, this library enables the use of the Pythonesque with-statement construct in C++. More broadly, this library seeks to formalize a generalized version of a subset of the RAII (Resource Acquisition is Initialization) idiom that, for the lack of preexisting terms, I shall refer here to as the STEP (Scope Triggered Event Processing) idiom. The STEP idiom is the commonly occurring use case where for a given block of code some user specified action(s) must execute on entry and on exit, and the failure to do so will result in some deterministic behavior." You can find the library here: https://github.com/m-w-a/WG Documentation is here: https://github.com/m-w-a/WG/tree/master/Local/Docs (In the same repository I've also redid Boost's local_function, but that the subject of another posting.) If there is enough interest from the community for such a library then I will go ahead and "Boostify" it.

I think Python's concept of Context and Context Managers useful and I've adapted, to the best of my abilities, a similar concept in C++. For those not familiar with this concept, I'll quote a brief snippet from the documentation:
"Simply stated, this library enables the use of the Pythonesque with-statement construct in C++. More broadly, this library seeks to formalize a generalized version of a subset of the RAII (Resource Acquisition is Initialization) idiom that, for the lack of preexisting terms, I shall refer here to as the STEP (Scope Triggered Event Processing) idiom. The STEP idiom is the commonly occurring use case where for a given block of code some user specified action(s) must execute on entry and on exit, and the failure to do so will result in some deterministic behavior."
You can find the library here:
Documentation is here:
https://github.com/m-w-a/WG/tree/master/Local/Docs
(In the same repository I've also redid Boost's local_function, but that the subject of another posting.)
If there is enough interest from the community for such a library then I will go ahead and "Boostify" it.
i'm sure you already know this but C++14's initialized lambda captures basically are python's with-as (albeit with C++'s normal syntax tax). struct foo { foo() { std::cout << "foo()" << std::endl; ~foo() { std::cout << "~foo()" << std::endl; }; auto make_foo() -> foo { return {}; } auto main() -> int { std::cout << "before" << std::endl; [f = make_foo()]() { std::cout << "during" << std::endl; }(); std::cout << "after" << std::endl; } With perfect forwarding we could even achieve a nicer syntax without any macros: with(make_foo(), [](auto f) { // ... });

On Mon, 17 Aug 2015 01:59:08 -0700, Sam Kellett <samkellett@gmail.com> wrote:
I think Python's concept of Context and Context Managers useful and I've adapted, to the best of my abilities, a similar concept in C++. For those not familiar with this concept, I'll quote a brief snippet from the documentation:
"Simply stated, this library enables the use of the Pythonesque with-statement construct in C++. More broadly, this library seeks to formalize a generalized version of a subset of the RAII (Resource Acquisition is Initialization) idiom that, for the lack of preexisting terms, I shall refer here to as the STEP (Scope Triggered Event Processing) idiom. The STEP idiom is the commonly occurring use case where for a given block of code some user specified action(s) must execute on entry and on exit, and the failure to do so will result in some deterministic behavior."
You can find the library here:
Documentation is here:
https://github.com/m-w-a/WG/tree/master/Local/Docs
(In the same repository I've also redid Boost's local_function, but that the subject of another posting.)
If there is enough interest from the community for such a library then I will go ahead and "Boostify" it.
i'm sure you already know this but C++14's initialized lambda captures basically are python's with-as (albeit with C++'s normal syntax tax).
I didn't know that. I was only targeting C++03/C++11.

I didn't know that. I was only targeting C++03/C++11.
This should work with C++11: template <typename T, typename F> void with(T &&t, F &&fn) { fn(std::forward<T>(t)); } with(make_foo(), [](foo &&f) { // do things with foo. }); // foo is destroyed. Don't think I can help w/r/t C++03 though, the lack of lambdas is a killer.

On Mon, 17 Aug 2015 09:48:55 -0700, Sam Kellett <samkellett@gmail.com> wrote:
I didn't know that. I was only targeting C++03/C++11.
This should work with C++11:
template <typename T, typename F> void with(T &&t, F &&fn) { fn(std::forward<T>(t)); }
with(make_foo(), [](foo &&f) { // do things with foo. }); // foo is destroyed.
Don't think I can help w/r/t C++03 though, the lack of lambdas is a killer.
One thing this method doesn't do is automatically determine if the scope exit prematurely.

This should work with C++11:
template <typename T, typename F> void with(T &&t, F &&fn) { fn(std::forward<T>(t)); }
with(make_foo(), [](foo &&f) { // do things with foo. }); // foo is destroyed.
Don't think I can help w/r/t C++03 though, the lack of lambdas is a killer.
One thing this method doesn't do is automatically determine if the scope exit prematurely.
hmm... scanning your documentation do you mean the scope_completed flag? ok so if i understand correctly you're saying that foo could be a transaction, and when foo is destructed you either want to commit or rollback depending on whether or not the lambda succeeded? interesting... an easy thing to do is an overload of with for objects of T that has an on_exit method to expect a boolean lambda and call the on_exit callback with the result of the lambda. namespace detail { // for types that can handle failure template <typename T, typename F> void with_impl(T &&t, F &&fn, std::true_type) { auto value(std::forward<T>(t)); const auto scope_completed(fn(value)); value.on_exit(scope_completed); } // for other types template <typename T, typename F> void with_impl(T &&t, F &&fn, std::false_type) { fn(std::forward<T>(t)); } } // namespace detail template <typename T, typename F> void with(T &&t, F &&fn) { const auto has_on_exit(boost::hana::is_valid([](auto&& x) -> decltype(( void) x.on_exit(true)) { }); detail::with_impl(std::forward<T>(t), std::forward<F>(fn), has_on_exit); } quick disclaimer: written this all straight into gmail without running it. it'll almost certainly have an error or two in but the general idea is sound as far as i can tell. also i've used Boost.Hana for determining the existence of the field, this is obviously c++14 only, but i put it in here because it's so beautifully concise and doesn't get in the way of what the example is actually trying to show. this is still possible in c++11 too i just couldn't be bothered (nor know off by heart) all the boilerplate that comes with such a check. then this should work: struct transaction { void on_exit(bool scope_completed) { if (scope_completed) { db.commit(); } else { db.rollback(); } } // rest of class... }; with(make_transaction(), [](transaction &&t) { // returning on success of operation. return t.insert_1000_rows_concurrently(..); }); // t will commit/rollback depending on success of nasty insert method. an potentially better (and simpler!) alternative could be to (optionally) accept another lambda argument that is the on_exit callback and to call that instead of the class' member function. // prototype: with(T &&t, F &&fn, G &&on_exit); ditto for on_enter too if you want preconditions

On 08/17/2015 11:59 AM, Sam Kellett wrote:
I think Python's concept of Context and Context Managers useful and I've adapted, to the best of my abilities, a similar concept in C++. For those not familiar with this concept, I'll quote a brief snippet from the documentation:
"Simply stated, this library enables the use of the Pythonesque with-statement construct in C++. More broadly, this library seeks to formalize a generalized version of a subset of the RAII (Resource Acquisition is Initialization) idiom that, for the lack of preexisting terms, I shall refer here to as the STEP (Scope Triggered Event Processing) idiom. The STEP idiom is the commonly occurring use case where for a given block of code some user specified action(s) must execute on entry and on exit, and the failure to do so will result in some deterministic behavior."
You can find the library here:
Documentation is here:
https://github.com/m-w-a/WG/tree/master/Local/Docs
(In the same repository I've also redid Boost's local_function, but that the subject of another posting.)
If there is enough interest from the community for such a library then I will go ahead and "Boostify" it.
i'm sure you already know this but C++14's initialized lambda captures basically are python's with-as (albeit with C++'s normal syntax tax).
struct foo { foo() { std::cout << "foo()" << std::endl; ~foo() { std::cout << "~foo()" << std::endl; };
auto make_foo() -> foo { return {}; }
auto main() -> int { std::cout << "before" << std::endl; [f = make_foo()]() { std::cout << "during" << std::endl; }(); std::cout << "after" << std::endl; }
With perfect forwarding we could even achieve a nicer syntax without any macros:
with(make_foo(), [](auto f) { // ... });
It's not the same. There is no way to conditionally return from the enclosing scope from the lambda: with(make_foo(), [](auto f) { if (!f) { return; } // ... }); is different from the python with/as construct.
participants (3)
-
Avi Kivity
-
Mostafa
-
Sam Kellett