mardi 26 septembre 2017

Wrapping standard library templates for DLL export (need sanity check)

I think I have a pretty good hold on the issues (different implementations, compiled binary differences, etc..) with exporting templates (vector, string, map, etc..) from the std library for a DLL (or lib) . However I find that I also dislike the use of custom vector/list/map classes and find creating an interface in classes to export their functionality to be tedious an repetitive, so I have come to the conclusion that I am just going to try and wrapper said objects to the best of my ability (and accept whatever performance impact that has). That said I was wondering if you all could sanity check what I have done so far.

So this is the road I have gone down (tested with vector first). I have created a class very similar to std::vector just named vector. The intent is to include it from another file inside a namespace so the the new vector doesn't conflict with anything else. I am working on an openCl library at the moment so that's what the code is coming from.

opencl_std.h

#ifndef _opencl_util_opencl_std_h
#define _opencl_util_opencl_std_h

#include "opencl_util_export.h"

#include <string>
#include <vector>

#ifdef opencl_util_EXPORTS
#define OPENCL_UTIL_EXTERN
#else
#define OPENCL_UTIL_EXTERN extern
#endif

namespace cl{namespace util
{

#include "std_wrappers\vector.h"

OPENCL_UTIL_EXTERN template class OPENCL_UTIL_EXPORT ::cl::util::vector<::size_t>;

}}//namespace cl::util

#endif //_opencl_util_opencl_std_h

opencl.cpp

#include "opencl_std.h"

namespace cl{namespace util
{

#include "std_wrappers\vector.cpp"

}}//namespace cl::util

OPENCL_UTIL_EXPORT is the obligatory dllimport/dllexport and OPENCL_UTIL_EXTERN sets the class to extern when importing. std_wrappers\vector.h includes the declaration of the class and std_wrappers\vector.cpp includes the definition. All though the class is a template the split declaration/definition works as the class is explicitly instantiated with this call (as well as exported).

OPENCL_UTIL_EXTERN template class OPENCL_UTIL_EXPORT ::cl::util::vector<::size_t>;

std_wrappers\vector.h

template<class _Ty, class _Alloc=std::allocator<_Ty> >
class vector
{
    typedef typename std::vector<_Ty, _Alloc>::value_type value_type;
    typedef typename std::vector<_Ty, _Alloc>::allocator_type allocator_type;
    typedef typename std::vector<_Ty, _Alloc>::size_type size_type;
    typedef typename std::vector<_Ty, _Alloc>::difference_type difference_type;

    typedef typename std::vector<_Ty, _Alloc>::reference reference;
    typedef typename std::vector<_Ty, _Alloc>::const_reference const_reference;
    typedef typename std::vector<_Ty, _Alloc>::pointer pointer;
    typedef typename std::vector<_Ty, _Alloc>::const_pointer const_pointer;

    typedef typename std::vector<_Ty, _Alloc>::iterator iterator;
    typedef typename std::vector<_Ty, _Alloc>::const_iterator const_iterator;
    typedef typename std::vector<_Ty, _Alloc>::reverse_iterator reverse_iterator;
    typedef typename std::vector<_Ty, _Alloc>::const_reverse_iterator const_reverse_iterator;

public:
    vector() noexcept;
    explicit vector(const _Alloc &alloc) noexcept;
    explicit vector(size_type count);
    vector(size_type count, const value_type &value);
    vector(size_type count, const value_type &value, const _Alloc &alloc);
    vector(const vector<_Ty, _Alloc> &right);
    vector(const vector<_Ty, _Alloc> &right, const _Alloc &alloc);
    vector(vector<_Ty, _Alloc> &&right) noexcept;
    vector(vector<_Ty, _Alloc> &&right, const _Alloc &alloc);

    ~vector();

    vector &operator=(const vector<_Ty, _Alloc> &other);
    vector &operator=(vector<_Ty, _Alloc> &&other);
    vector &operator=(std::initializer_list<_Ty> ilist);

    void assign(size_type count, const _Ty &value);
//    template< class InputIt> void assign(InputIt first, InputIt last);
    void assign(std::initializer_list<_Ty> ilist);

    reference at(size_type pos);
    const_reference at(size_type pos) const;

    reference operator[](size_type pos);
    const_reference operator[](size_type pos) const;

    reference front();
    const_reference front() const;

    reference back();
    const_reference back() const;

    _Ty *data() noexcept;
    const _Ty *data() const noexcept;

    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    const_iterator cbegin() const noexcept;

    iterator end() noexcept;
    const_iterator end() const noexcept;
    const_iterator cend() const noexcept;

    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const;
    const_reverse_iterator crbegin() const noexcept;

    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;
    const_reverse_iterator crend() const noexcept;

    bool empty() const noexcept;
    size_type size() const noexcept;
    void reserve(size_type new_cap);
    void clear() noexcept;

    void push_back(const _Ty &value);
    void push_back(_Ty &&value);

    void resize(size_type count);
    void resize(size_type count, const value_type& value);

private:
    std::vector<_Ty, _Alloc> *_vector;
};

std_wrappers\vector.cpp

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector() noexcept { _vector=new std::vector<_Ty, _Alloc>(); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(const _Alloc &alloc) noexcept { _vector=new std::vector<_Ty, _Alloc>(alloc); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(size_type count) { _vector=new std::vector<_Ty, _Alloc>(count); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(size_type count, const value_type &value) { _vector=new std::vector<_Ty, _Alloc>(count, value); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(size_type count, const value_type& value, const _Alloc &alloc) { _vector=new std::vector<_Ty, _Alloc>(count, value, alloc); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(const vector<_Ty, _Alloc> &right) { _vector=new std::vector<_Ty, _Alloc>(*(right._vector)); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(const vector<_Ty, _Alloc> &right, const _Alloc &alloc) { _vector=new std::vector<_Ty, _Alloc>(*(right._vector), alloc); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(vector<_Ty, _Alloc> &&right) noexcept { _vector=right._vector; right._vector=new std::vector<_Ty, _Alloc>();  }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(vector<_Ty, _Alloc> &&right, const _Alloc &alloc) { _vector=right._vector; right._vector=new std::vector<_Ty, _Alloc>(alloc); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::~vector() { delete _vector; }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc> &vector<_Ty, _Alloc>::operator=(const vector<_Ty, _Alloc> &other) { _vector->operator=(*(other._vector)); return *this;}

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc> &vector<_Ty, _Alloc>::operator=(vector<_Ty, _Alloc> &&other) { delete _vector; _vector=other._vector; other._vector=new std::vector<_Ty, _Alloc>(); return *this; }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc> &vector<_Ty, _Alloc>::operator=(std::initializer_list<_Ty> ilist) { _vector->operator=(ilist); return *this; }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::assign(size_type count, const _Ty &value) { _vector->assign(count, value); }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::assign(std::initializer_list<_Ty> ilist) { _vector->assign(ilist); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::reference vector<_Ty, _Alloc>::at(size_type pos) { return _vector->at(pos); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reference vector<_Ty, _Alloc>::at(size_type pos) const { return _vector->at(pos); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::reference vector<_Ty, _Alloc>::operator[](size_type pos) { return _vector->operator[](pos); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reference vector<_Ty, _Alloc>::operator[](size_type pos) const { return _vector->operator[](pos); };

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::reference vector<_Ty, _Alloc>::front() { return _vector->front(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reference vector<_Ty, _Alloc>::front() const { return _vector->front(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::reference vector<_Ty, _Alloc>::back() { return _vector->back(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reference vector<_Ty, _Alloc>::back() const { return _vector->back(); }

template<class _Ty, class _Alloc>
_Ty *vector<_Ty, _Alloc>::data() noexcept { return _vector->data(); }

template<class _Ty, class _Alloc>
const _Ty *vector<_Ty, _Alloc>::data() const noexcept { return _vector->data(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::iterator vector<_Ty, _Alloc>::begin() noexcept { return _vector->begin(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_iterator vector<_Ty, _Alloc>::begin() const noexcept { return _vector->begin(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_iterator vector<_Ty, _Alloc>::cbegin() const noexcept { return _vector->cbegin(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::iterator vector<_Ty, _Alloc>::end() noexcept { return _vector->end(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_iterator vector<_Ty, _Alloc>::end() const noexcept { return _vector->end(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_iterator vector<_Ty, _Alloc>::cend() const noexcept { return _vector->cend(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::reverse_iterator vector<_Ty, _Alloc>::rbegin() noexcept { return _vector->rbegin(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reverse_iterator vector<_Ty, _Alloc>::rbegin() const { return _vector->rbegin(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reverse_iterator vector<_Ty, _Alloc>::crbegin() const noexcept { return _vector->crbegin(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::reverse_iterator vector<_Ty, _Alloc>::rend() noexcept { return _vector->rend(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reverse_iterator vector<_Ty, _Alloc>::rend() const noexcept { return _vector->rend(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::const_reverse_iterator vector<_Ty, _Alloc>::crend() const noexcept { return _vector->rend(); }

template<class _Ty, class _Alloc>
bool vector<_Ty, _Alloc>::empty() const noexcept { return _vector->empty(); }

template<class _Ty, class _Alloc>
typename vector<_Ty, _Alloc>::size_type vector<_Ty, _Alloc>::size() const noexcept { return _vector->size(); }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::reserve(size_type new_cap) { _vector->reserve(new_cap); }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::clear() noexcept { _vector->clear(); }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::push_back(const _Ty &value) { _vector->push_back(value); }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::push_back(_Ty &&value) { _vector->push_back(value); }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::resize(size_type count) { _vector->resize(count); }

template<class _Ty, class _Alloc>
void vector<_Ty, _Alloc>::resize(size_type count, const value_type& value) { _vector->resize(count, value); }

I left out a few functions as I don't believe I can make them work (basically any function that has its own template)

template< class InputIt > vector( InputIt first, InputIt last, 
    const Allocator& alloc = Allocator() );
template< class InputIt > void assign( InputIt first, InputIt last );

and I had to take some care with the rvalue reference function but I think I got it correct.

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(vector<_Ty, _Alloc> &&right) noexcept { _vector=right._vector; right._vector=new std::vector<_Ty, _Alloc>();  }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc>::vector(vector<_Ty, _Alloc> &&right, const _Alloc &alloc) { _vector=right._vector; right._vector=new std::vector<_Ty, _Alloc>(alloc); }

template<class _Ty, class _Alloc>
vector<_Ty, _Alloc> &vector<_Ty, _Alloc>::operator=(vector<_Ty, _Alloc> &&other) { delete _vector; _vector=other._vector; other._vector=new std::vector<_Ty, _Alloc>(); return *this; }

I have compiled and ran this all in VS2015 and everything seems kosher (granted thats not saying much for the rest of the compilers). I am getting a C4661 warning on the explicit template instantiation.

opencl_stl.h(21): warning C4661: 'void cl::util::vector<std::size_t,std::allocator<std::_Ty>>::resize(unsigned __int64)': no suitable definition provided for explicit template instantiation request

but I think that it is purely a warning as it is defined just not before the instantiation happens. I expected the C4251 warning from this line,

std::vector<_Ty, _Alloc> *_vector;

but I didn't see one, considering it is a pointer the compiled versions should agree on its size anyway. Using cl::util::vector<::size_t> from the library works fine. I have even tried to use cl::util::vector and got link warnings as I expected (as it was never instantiated). I plan to do this with some of the other templates, list, map, unordered_map, string, etc... just looking for a check if I missed something.

Aucun commentaire:

Enregistrer un commentaire