ndk icon indicating copy to clipboard operation
ndk copied to clipboard

C++ Wrapper for jstring

Open arturbac opened this issue 6 years ago • 1 comments

Forked from #476

Proposal for jstring C++ wrapper char16_t wrapper is commented out in example as it requires some utf32-utf16 convertion

jstring.hpp

#pragma once
#include "common.h"
#include <iostream>
#include <vector>
#include <string>


using jenv = JNIEnv *;

namespace jni
{
  ///\brief resource managment type
  enum struct jownership_e : uint8_t 
    { 
    external, ///\brief external owned not managment
    owned  ///\brief internaly managed
    };
  enum struct attach_mode_e : uint8_t 
    { 
    attached, ///\brief after intance is created internal java buffers are auto attached for jstring, jarray
    detached  //\brief after intance is created internal java buffers are not attached
    };

  template<typename CharType>
  struct char_traits : public std::char_traits<CharType>
    {};
    
  template<>
  struct char_traits<char> : public std::char_traits<char>
    {
    inline static char const * jatatch_string(jenv env, ::jstring instance) noexcept 
      { return env->GetStringUTFChars(instance, nullptr); }
      
    inline static void detach_string(jenv env, ::jstring instance, char const * ptr ) noexcept
      { env->ReleaseStringUTFChars(instance, ptr); }
      
    inline static ::jstring construct( jenv env, char const * src , size_t sz = 0u) noexcept 
      { return env->NewStringUTF(src); }
      
      
    inline static size_t length(jenv env, ::jstring s, char_type const * p) noexcept { return std::char_traits<char>::length(p); }
    };
#if 0
  template<>
  struct char_traits<char16_t> : public std::char_traits<char16_t>
    {
    inline static char16_t const * jatatch_string(jenv env, ::jstring instance) noexcept 
      { return reinterpret_cast<char16_t const *>(env->GetStringChars(instance, nullptr)); }
      
    inline static void detach_string(jenv env, ::jstring instance, char16_t const * ptr ) noexcept
      { env->ReleaseStringChars(instance, reinterpret_cast<jchar const *>(ptr)); }
      
    inline static ::jstring construct( jenv env, char16_t const * src ) noexcept 
      { return env->NewString(reinterpret_cast<jchar const *>(src), std::char_traits<char16_t>::length(src) ); }
      
    inline static ::jstring construct( jenv env, char16_t const * src, size_t sz ) noexcept 
      { return env->NewString(reinterpret_cast<jchar const *>(src),sz); }
  
    //charset unused
    inline static ::jstring construct( jenv env, wchar_t const * src, size_t sz, uint8_t charset = 0 ) noexcept 
      {
      Aqurat::u16_char_buffer_type buff;
      size_t const code_points_count { Aqurat::WasU16(src, src + sz, buff) };
      if( !buff.empty())
        return env->NewString(reinterpret_cast<jchar const *>(buff.data()), code_points_count ); 
      return nullptr;
      }
    ///\warning u16 java string is not nullterminated
    inline static size_t length(jenv env, ::jstring s, char_type const * p) noexcept { return env->GetStringLength(s); }
    };
#endif
  
  template<typename CharType, typename CharTraits = char_traits<CharType>>
  class basic_string
    {
  public:
    using char_type = CharType;
    using char_traits = CharTraits;
    using const_pointer = char_type const *;
    using const_iterator = const_pointer;
    using native_string_type = std::basic_string<char_type>;
    
  private:
    jenv          env_;
    ::jstring     instance_;
    const_pointer begin_, end_;
    jownership_e  owns_object_;
    
  public:
    inline explicit operator bool() const noexcept             { return nullptr != instance_ ; }
    inline char_type operator[]( size_t ix ) const noexcept    { return begin_[ix]; }
    inline operator native_string_type() const                 { return native_string_type{ c_str(), size() }; }
    inline ::jstring get() const noexcept                      { return instance_; }
    inline explicit operator ::jstring() const noexcept        { return instance_; }
    inline explicit operator ::jobject() const noexcept        { return instance_; }
    inline bool            empty() const noexcept              { return begin_ == end_; }
    inline size_t          size() const noexcept               { return static_cast<size_t>(std::distance(begin(), end())); }
    inline const_pointer   data() const noexcept               { return begin_; }
    ///\warning u16 string is not null terminated
    inline const_pointer   c_str() const noexcept              { return begin_; }
    inline const_iterator  begin() const noexcept              { return begin_; }
    inline const_iterator  end() const noexcept                { return end_; }
    
    inline basic_string() noexcept : 
        env_(), instance_(), begin_(), end_(), owns_object_()
      {}
    ///\param mode use attach_mode_e::detached if string will be used only as argument to jni methods
    template<typename friend_char_type>
    basic_string(jenv env, size_t size, friend_char_type const * src, attach_mode_e mode = attach_mode_e::attached ) noexcept;

    ///\warning this constructor should be used only with char ansi, with char16 is ineffective, will calculate length
    ///\param src null terminated string
    ///\param mode use attach_mode_e::detached if string will be used only as argument to jni methods
    basic_string(jenv env, char_type const * src, attach_mode_e mode = attach_mode_e::attached ) noexcept;
    
    inline basic_string(jenv env, native_string_type const & src, attach_mode_e mode = attach_mode_e::attached ) noexcept :
        basic_string( env, src.size(), src.c_str(), mode )
      {}

    ///\warning if \param instance was obtained from call to java method in native code, set \param own to jownership_e::owned
    ///\param mode use attach_mode_e::detached if string will be used only as argument to jni methods
    basic_string(jenv env, ::jstring instance, jownership_e own = jownership_e::external, 
                 attach_mode_e mode = attach_mode_e::attached  ) noexcept;
                 
    basic_string( basic_string && rh ) noexcept;
    basic_string & operator=( basic_string && rh ) noexcept;
    ~basic_string() { reset(); }
    
    void reset() noexcept;
    
    ///\brief releases repsponsibility for object, ex when returning obejct to java that was constructed in native code
    ::jstring release() noexcept;
    };
    
  using string = basic_string<char>;
//   using u16string = basic_string<char16_t>;
  
  inline std::ostream & operator << ( std::ostream & outstr, jni::string const & src )
    {
    outstr.write( src.c_str(), static_cast<ptrdiff_t>( src.size()) );
    return outstr;
    }

    
  template<typename T, typename U>
  template<typename friend_char_type>
  basic_string<T,U>::basic_string(jenv env, size_t size, friend_char_type const * src, attach_mode_e mode ) noexcept : 
      env_(env), instance_( char_traits::construct(env,src,size) ),
      begin_(), end_(), owns_object_(jownership_e::owned)
    {
    if(nullptr != instance_ && attach_mode_e::attached == mode)
      {
      begin_ = char_traits::jatatch_string(env, instance_);
      if(nullptr != begin_)
        end_= std::next(begin_, static_cast<ptrdiff_t>(char_traits::length(env,instance_,begin_)) );
      }
    }
    
  extern template class basic_string<char>;
}

jstring.cc

#include <jstring.hpp>

namespace jni
{
 
  template<typename T, typename U>
  basic_string<T,U>::basic_string(jenv env, char_type const * src, attach_mode_e mode ) noexcept : 
      env_(env), instance_( char_traits::construct(env,src) ),
      begin_(), end_(), owns_object_(jownership_e::owned)
    {
    if( nullptr != instance_ && attach_mode_e::attached == mode )
      {
      begin_ = char_traits::jatatch_string(env, instance_);
      if(nullptr != begin_)
        end_= std::next(begin_, static_cast<ptrdiff_t>( char_traits::length(env,instance_,begin_)) );
      }
    }
    
  template<typename T, typename U>
  basic_string<T,U>::basic_string( basic_string && rh ) noexcept :
      env_(rh.env_), instance_(rh.instance_), begin_(rh.begin_), end_(rh.end_), owns_object_(rh.owns_object_)
    {
    rh.env_ = nullptr;
    rh.instance_ = nullptr;
    rh.begin_ = nullptr;
    rh.end_ = nullptr;
    rh.owns_object_ = jownership_e::external;
    }
    
  template<typename T, typename U>
  basic_string<T,U>::basic_string(jenv env, ::jstring instance, jownership_e own, attach_mode_e mode ) noexcept : 
      env_(env), instance_(instance), begin_(), end_(), owns_object_(own) 
    {
    if(nullptr != instance_ && attach_mode_e::attached == mode)
      {
      begin_ = char_traits::jatatch_string(env, instance_);
      if(nullptr != begin_)
        end_= std::next(begin_, static_cast<ptrdiff_t>(char_traits::length(env,instance_,begin_)) );
      }
    }
      
  template<typename T, typename U>
  basic_string<T,U> & 
  basic_string<T,U>::operator=( basic_string && rh ) noexcept
    {
    std::swap(env_,rh.env_);
    std::swap(instance_,rh.instance_);
    std::swap(begin_,rh.begin_);
    std::swap(end_,rh.end_);
    std::swap(owns_object_,rh.owns_object_);
    return *this;
    }
      
  template<typename T, typename U>
  ::jstring basic_string<T,U>::release() noexcept
    {
    ::jstring ret { instance_ };
    if( nullptr != instance_ )
      {
      if(nullptr != begin_)
        {
        char_traits::detach_string(env_, instance_, begin_ );
        begin_ = nullptr;
        end_ = nullptr;
        }
      env_ = nullptr;
      owns_object_ = jownership_e::external;
      instance_ = nullptr;
      }
    return ret;
    }
    
  template<typename T, typename U>
  void basic_string<T,U>::reset() noexcept
    {
    if( nullptr != instance_ )
      {
      if(nullptr != begin_)
        {
        char_traits::detach_string(env_, instance_, begin_ );
        begin_ = nullptr;
        end_ = nullptr;
        }
      if(owns_object_ == jownership_e::owned)
        env_->DeleteLocalRef(instance_);
      instance_ = nullptr;
      env_ = nullptr;
      }
    }
    
  template class basic_string<char>;
}

arturbac avatar Oct 25 '19 12:10 arturbac