Thursday, February 20, 2014

Efficient string arguments passing to C-functions

I often have to deal with the C-language libraries and the first thing I do in such case is writing a C++ wrapper to abstract my logic from C-style memory management, error handling, etc. One of the commonplaces here is work with strings. Wrapper of C-function with string parameters should support both std::strings and the string literals (compare this with constructors of standard exception classes which are overloaded to accept const char* or const std::string& error message).

Consider the following function:
int libfoo_do_smth(libfoo_context_t* context, const char* x); //returns TRUE on success

Wrapper should be something like
void libfoo::context::do_smth(const char* x) {
 if(!libfoo_do_smth(m_context, x))
  throw libfoo::exception("do_smth failed");
};

template<class CharTraits, class Allocator>
void libfoo::context::do_smth(const std::basic_string<char, CharTraits, Allocator>& x) {
 do_smth(x.data());
};
It's OK (except that the second overload is trivial). But what if libfoo_do_smth is taking two string arguments? Or maybe three ones ore more? Even in case of two arguments we need four overloads and we'll confront with a combinatorial explosion of number of trivial overloads increasing number of string arguments. To solve this problem I use a simple class that is a zero-ended string wrapper. It mimics the std::string interface but neither owns the memory used to store array of char nor knows the length of the string it refers to.
template<typename Char>
struct basic_const_c_string {
 template<typename C>
 basic_const_c_string(const basic_const_c_string<C> string) :
  m_data{string.data()}
 {};

 basic_const_c_string(const Char* string) :
  m_data{string}
 {
  if(!string) throw std::invalid_argument("null-pointer initialization");
 };

 template<typename CharTraits, typename Allocator>
 basic_const_c_string(
  const std::basic_string<
   typename std::remove_const<Char>::type, CharTraits, Allocator
  >& string
 ) :
  m_data{string.data()}
 {};

 template<typename C, typename Traits, typename Allocator>
 basic_const_c_string(std::basic_string<C, Traits, Allocator>&&) = delete;

 const Char* data() const {
  return m_data;
 };

 const Char* c_str() const {
  return data();
 };

private:
 const Char* m_data;
}; //basic_const_c_string

typedef basic_const_c_string<char>    const_c_string;
typedef basic_const_c_string<wchar_t> const_c_wstring;
Now there's no need in overloads and our wrapper may look like following:
void libfoo::context::do_smth(const_c_string x) {
 if(!libfoo_do_smth(m_context, x.data()))
  throw libfoo::exception("do_smth failed");
};
Obviously there's no need in overloads in case of any number of string parameters. Now however we cannot pass a temporary std::string parameter to our function. Corresponding constructor of basic_const_c_string is deleted because we cannot move std::string to basic_const_c_string (it doesn't owns memory). To circumvent this I use combination of universal references and overloading:
template<typename Char, typename String>
basic_const_c_string<Char>
make_const_string(String&& string) {
 return {string};
};

template<typename Char, typename CharTraits, typename Allocator>
const std::basic_string<Char, CharTraits, Allocator>
make_const_string(std::basic_string<Char, CharTraits, Allocator>&& string) {
 return {string};
};
So the wrapper code becomes (in case of two string parameters):
template<class String1, class String2>
void libfoo::context::do_smth_else(String1&& x, String2&& y) {
 auto temp_x = make_const_string<char>(std::forward<String1>(x));
 auto temp_y = make_const_string<char>(std::forward<String2>(y));

 if(!libfoo_do_smth_else(m_context, temp_x.data(), temp_y.data()))
  throw libfoo::exception("do_smth_else failed");
};
Well, now we need one template parameter for one string argument, but it's look better than four trivial overloads of one function.

Thursday, August 8, 2013

Problem with expected<T>

The realization of expected<T> in this blog has a bug. While using it I've write a code like follows:

expected<double> my_sqrt(double x) {
    if(x < 0) return expected<int>::from_exception(std::out_of_range("x"));
    return std::sqrt(x);
};

int main() {
    std::cout << *sqrt(-10) << std::endl;
};

You may suspect that the first variant of my_sqrt was returning expected<int> and I've just forgot to change types everywhere. I've gotten "0" instead of "Terminate called after...". There was called a ctor expected<double>::expected(AA... arguments) which had taken an instance of expected<int> as an argument and succeeded.

To avoid such mistakes following code should be added to expected<T> (not expected_common<T>):

template<typename U>
expected(expected<U>) = delete;

Monday, May 27, 2013

Extending expected<T> to deal with references.

On Dec 10, 2012 Andrei Alexandrescu presented the concept of expected class template which purpose is to hold a value returned by function, or an exception object in case of function failure (similar to Boost.Optional, but latter holds only a value, no error information). The main idea is that expected<T> contains an anonymous union with members of types T and std::exception_ptr. Unions with class object data members are available in C++11.

However, unions still cannot contain reference variables as the members [ibid.], so expected is useless for functions returning references (like at() method of std::vector, et c.). A possible solution is to partially specialize expected<T&> and to use std::reference_wrapper<T> instead of T as union member. Also, such specialized class should use different constructors set since it's senseless to initialize reference with anything but other reference. Same is for expected<const T&> with addition that it's get() method should always return constant reference.

Below is corresponding realization of expected class template. It some differs from original one (e.g. pointer-like operators is used instead of is_valid() and get() methods), but substantially they are the same.

#include <functional>
#include <stdexcept>

template<typename>
class expected;

template<typename>
class expected_common;

template<typename T>
class expected_traits {
    friend class expected_common<T>;

    typedef T  storage;
    typedef T  value;
    typedef T* pointer;
    typedef T& reference;
}; //expected_traits

template<typename T>
class expected_traits<T&> {
    friend class expected_common<T&>;

    typedef std::reference_wrapper<T> storage;
    typedef T                         value;
    typedef T*                        pointer;
    typedef T&                        reference;
}; //expected_traits<T&>

template<typename T>
class expected_traits<const T&> {
    friend class expected_common<const T&>;

    typedef std::reference_wrapper<const T> storage;
    typedef T                               value;
    typedef const T*                        pointer;
    typedef const T&                        reference;
}; //expected_traits<const T&>

template<typename T>
class expected_common {
    friend class expected<T>;

    typedef typename expected_traits<T>::value     value;
    typedef typename expected_traits<T>::storage   storage;
    typedef typename expected_traits<T>::pointer   pointer;
    typedef typename expected_traits<T>::reference reference;

    expected_common() {};

    expected_common(const value& value) :
        m_valid  (true),
        m_storage(value)
    {};

    expected_common(value& value) :
        m_valid  (true),
        m_storage(value)
    {};

    expected_common(value&& value) :
        m_valid  (true),
        m_storage(std::move(value))
    {};

    template<typename... AA>
    explicit expected_common(AA&&... arguments) :
        m_valid  (true),
        m_storage(std::forward<AA>(arguments)...)
    {};

    expected_common(const expected_common& other) :
        m_valid(other.m_valid)
    {
        if(m_valid) new(&m_storage) storage(other.m_storage);
        else new(&m_exception) std::exception_ptr(other.m_exception);
    };

    expected_common(expected_common&& other) :
        m_valid(other.m_valid)
    {
        if(m_valid) new(&m_storage) storage(std::move(other.m_storage));
        else new(&m_exception) std::exception_ptr(std::move(other.m_exception));
    };

    ~expected_common() {
        if(m_valid) m_storage.~storage();
        else m_exception.~exception_ptr();
    };

    void swap(expected_common& other) {
        if(m_valid) {
            if(other.m_valid) std::swap(m_storage, other.m_storage);
            else {
                auto exception = std::move(other.m_exception);
                new(&other.m_storage) storage(std::move(m_storage));
                new(&m_exception) std::exception_ptr(exception);
            }
        } else
            if(other.m_valid) other.swap(*this);
            else m_exception.swap(other.m_exception);
    };

public:
    template<class E>
    static expected<T> from_exception(const E& exception) {
        if (typeid (exception) != typeid (E))
        throw std::invalid_argument("slicing detected");

        return from_exception(std::make_exception_ptr(exception));
    }

    static expected<T> from_exception(std::exception_ptr exception) {
        expected<T> result;
        result.m_valid = false;
        new(&result.m_exception) std::exception_ptr(exception);
        return result;
    };

    static expected<T> from_exception() {
        return from_exception(std::current_exception());
    };

    template<class F, typename... AA>
    static expected<T> from_code(F function, AA&&... arguments) {
        try {
            return expected<T>(function(std::forward<AA>(arguments)...));
        }
        catch (...) {
            return from_exception();
        };
    };

    operator bool() const {
        return m_valid;
    };

    template<class E>
    bool exception_is() const {
        try {
            if(!m_valid) std::rethrow_exception(m_exception);
        } catch(const E&) {
            return true;
        } catch(...) {};

        return false;
    };

private:
    reference get() {
        if(!m_valid) std::rethrow_exception(m_exception);
        return m_storage;
    };

    const reference get() const {
        if(!m_valid) std::rethrow_exception(m_exception);
        return m_storage;
    };

public:
    reference operator * () {
        return get();
    };

    const reference operator * () const {
        return get();
    };

    pointer operator -> () {
        return &get();
    };

    const pointer operator -> () const {
        return &get();
    };

private:
    bool                   m_valid;
    union {
        storage            m_storage;
        std::exception_ptr m_exception;
    };
}; //expected_common

template<typename T>
class expected :
    public expected_common<T>
{
    friend class expected_common<T>;
    typedef expected_common<T> common;

    expected() {};

public:
    template<class... AA>
    expected(AA&&... arguments) :
        common(std::forward<AA>(arguments)...)
    {};

    expected(const T& value) :
        common(value)
    {};

    expected(T&& value) :
        common(value)
    {};

    expected(const expected& other) :
        common(static_cast<const common&>(other))
    {};

    expected(expected&& other) :
        common(static_cast<common&&>(other))
    {};

    void swap(expected& other) {
        common::swap(static_cast<common&>(other));
    };
}; //expected

template<typename T>
class expected<T&> :
    public expected_common<T&>
{
    friend class expected_common<T&>;
    typedef expected_common<T&> common;

    expected() {};

public:
    expected(T& value) :
        common(value)
    {};

    expected(const expected& other) :
        common(static_cast<const common&>(other))
    {};

    expected(expected&& other) :
        common(static_cast<common&&>(other))
    {};

    void swap(expected& other) {
        common::swap(static_cast<common&>(other));
    };
}; //expected<T&>

template<typename T>
class expected<const T&> :
    public expected_common<const T&>
{
    friend class expected_common<const T&>;
    typedef expected_common<const T&> common;

    expected() {};

public:
    expected(const T& value) :
        common(value)
    {};

    expected(const expected& other) :
        common(static_cast<const common&>(other))
    {};

    expected(expected&& other) :
        common(static_cast<common&&>(other))
    {};

    void swap(expected& other) {
        common::swap(static_cast<common&>(other));
    };
}; //expected<const T&>