CppCon 2015: Andrei Alexandrescu “Declarative Control Flow"

By: CppCon

410   3   40559

Uploaded on 10/18/2015

http://www.Cppcon.org

Presentation Slides, PDFs, Source Code and other presenter materials are available at: https://github.com/cppcon/cppcon2015

Getting exception handling right is a perennial problem in C++ that has eluded systematization. Not for much longer. New language and library developments make it possible to handle exceptions in a declarative manner, leading to drastic code simplification.

This talk discusses an alternative approach to handling exceptional flow that eliminates the need for small ancillary RAII classes, try/catch statements that rethrow, and other cleanup mechanisms. The popular Scope Guard idiom gets a spectacular generalization. Statements specify in a declarative manner actions to be taken if the current scope is left normally or via an exception. The resulting code is simpler, smaller, and easier to maintain.

Andrei Alexandrescu is a researcher, software engineer, and author. He wrote three best-selling books on programming (Modern C++ Design, C++ Coding Standards, and The D Programming Language) and numerous articles and papers on wide-ranging topics from programming to language design to Machine Learning to Natural Language Processing. Andrei holds a PhD in Computer Science from the University of Washington and a BSc in Electrical Engineering from University "Politehnica" Bucharest. He works as a Research Scientist for Facebook.Website: http://erdani.comTwitter handle: @incomputable

Videos Filmed & Edited by Bash Films: http://www.BashFilms.com

Comments (4):

By anonymous    2017-09-20

Does that mean I experienced gpu memory leak?

Yes, it does. You must not allocate resources without ensuring they will eventually be deallocated; and by throwing an exception if the cudaMemCpy() fails - you're not making that assurance.

OR somehow throw, try and catch can handle this situation?

Actually, yes, sort of.. as @Jean-BaptisteYunès suggests, RAII is key. Please read this:

What destructors are run when the constructor throws an exception?

So, if you could shove your memory allocation and de-allocation into a RAII member of your foo class, you would have completed its construction, and thus its destructor - which deallocates - would have run on exiting the foo() scope, even with an exception.

At this point I'll say that you're reinventing the wheel with some of the code you're writing. You can find both a mechanism for wrapping CUDA errors with exceptions and a unique-pointer-like RAII holder for your allocated memory in my cuda-api-wrappers library*. So you would have something like:

class foo {
public:
    using element_type = float;
    enum : size_t { num_elements };
protected:
    struct {
        cuda::memory::device::unique_ptr<element_type> device;
        cuda::memory::host::unique_ptr<element_type>   host;
    } data;

public:
    foo() : data( {
        cuda::memory::device::make_unique<element_type[]>(
            cuda::device::default_device_id, 
            num_elements
        ),
        cuda::memory::host::make_unique(num_elements)
    } )
    {
        // Do something...
        cuda::memory::copy(
            data.host.get(), data.device.get(), num_elements * sizeof(element_type)
        );
        // Do something...
    }
    ~foo() {
        // Do something...

        // ERRCHK(cudaFree(dev_floatArray));
        // No need to free anything! It's magic!

        // ERRCHK(cudaDeviceReset());
        // Don't reset your device you really need to - and
        // you don't need to.
    }
}

Another approach you could consider, instead of a RAII class for holding memory, is Andrei Alexandrescu's 'Scope Guard' mechanism. He explains it (well, the latest version of it) in this video:

CppCon 2015: Andrei Alexandrescu - Declarative Control Flow

Is it a good idea to throw exceptions everywhere, and catch them only in main() function?

Make that a separate question, because the answer is not a simple yes/no. Actually, there are enough questions and answers here on SO which cover that, I think.

* - Other libraries may also provide something similar, e.g. Thrust, perhaps; but with this one you're not bound to complex abstractions, just CUDA Runtime API wrappers.)

Original Thread

By anonymous    2017-09-20

Caution: this solution comes without the guarantee that your code reviewer will like it.

We can use a trick similar to the one Alexandrescu uses for his SCOPE_EXIT macro (awesome one-hour conference, this bit is at 18:00).

The gist of it: a clever macro and a dismembered lambda.

namespace myFunction_detail {
    struct Header {
        // Data from the construct's header
    };

    template <class F>
    void operator * (Header &&header, F &&body) {
        // Do something with the header and the body
    }
}

#define myPrefix_myFunction(a, b, c) \
    myFunction_detail::Header{a, b, c} * [&]

Using it as follows:

myPrefix_myFunction(foo, bar, baz) {

}; // Yes, we need the semicolon because the whole thing is a single statement :/

... reconstitutes a complete lambda after macro expansion, and lands into myFunction_detail::operator* with acess to foo, bar, baz, and the body of the construct.

Original Thread

Recommended Books

    Submit Your Video

    If you have some great dev videos to share, please fill out this form.