|
|
|
|
@ -13,7 +13,7 @@
@@ -13,7 +13,7 @@
|
|
|
|
|
#include <string> |
|
|
|
|
#include <ostream> |
|
|
|
|
#include <vcclr.h> |
|
|
|
|
#include <msclr/marshal.h> |
|
|
|
|
#include <msclr/marshal_cppstd.h> |
|
|
|
|
|
|
|
|
|
public interface class ICppInstance |
|
|
|
|
{ |
|
|
|
|
@ -24,198 +24,286 @@ public interface class ICppInstance
@@ -24,198 +24,286 @@ public interface class ICppInstance
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// CLI extensions namespace
|
|
|
|
|
namespace clix { |
|
|
|
|
namespace msclr { |
|
|
|
|
namespace interop { |
|
|
|
|
namespace details |
|
|
|
|
{ |
|
|
|
|
inline _Check_return_ size_t GetUTF8StringSize(System::String^ _str) |
|
|
|
|
{ |
|
|
|
|
cli::pin_ptr<const wchar_t> _pinned_ptr = PtrToStringChars(_str); |
|
|
|
|
|
|
|
|
|
/// <summary>Encoding types for strings</summary>
|
|
|
|
|
enum Encoding { |
|
|
|
|
auto _size = static_cast<size_t>( |
|
|
|
|
::WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS, _pinned_ptr, |
|
|
|
|
_str->Length, NULL, 0, NULL, NULL)); |
|
|
|
|
if (_size == 0 && _str->Length != 0) |
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentException(_EXCEPTION_WC2MB); |
|
|
|
|
} |
|
|
|
|
// adding 1 for terminating nul
|
|
|
|
|
_size += 1; |
|
|
|
|
return _size; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>ANSI encoding</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is the default encoding you've most likely been using all around in C++. ANSI
|
|
|
|
|
/// means 8 Bit encoding with character codes depending on the system's selected code page.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
E_ANSI, |
|
|
|
|
inline _Check_return_ size_t GetUnicodeStringSizeUTF8Source(_In_reads_z_(_count + 1) const char* _str, size_t _count) |
|
|
|
|
{ |
|
|
|
|
if (_count > INT_MAX) |
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>UTF-8 encoding</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is the encoding commonly used for multilingual C++ strings. All ASCII characters
|
|
|
|
|
/// (0-127) will be represented as single bytes. Be aware that UTF-8 uses more than one
|
|
|
|
|
/// byte for extended characters, so std::string::length() might not reflect the actual
|
|
|
|
|
/// length of the string in characters if it contains any non-ASCII characters.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
E_UTF8, |
|
|
|
|
auto _size = static_cast<size_t>(::MultiByteToWideChar(CP_UTF8, 0, _str, static_cast<int>(_count), NULL, 0)); |
|
|
|
|
if (_size == 0 && _count != 0) |
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentException(_EXCEPTION_MB2WC); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>UTF-16 encoding</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is the suggested to be used for marshaling and the native encoding of .NET
|
|
|
|
|
/// strings. It is similar to UTF-8 but uses a minimum of two bytes per character, making
|
|
|
|
|
/// the number of bytes required for a given string better predictable. Be aware, however,
|
|
|
|
|
/// that UTF-16 can still use more than two bytes for a character, so std::wstring::length()
|
|
|
|
|
/// might not reflect the actual length of the string.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
E_UTF16, E_UNICODE = E_UTF16 |
|
|
|
|
//adding 1 for terminating nul
|
|
|
|
|
_size += 1; |
|
|
|
|
return _size; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
inline void WriteUTF8String(_Out_writes_all_(_size) _Post_z_ char* _buf, size_t _size, System::String^ _str) |
|
|
|
|
{ |
|
|
|
|
cli::pin_ptr<const wchar_t> _pinned_ptr = PtrToStringChars(_str); |
|
|
|
|
|
|
|
|
|
// Ignore this if you're just scanning the headers for informations!
|
|
|
|
|
/* All this template stuff might seem like overkill, but it is well thought out and enables
|
|
|
|
|
you to use a readable and convenient call while still keeping the highest possible code |
|
|
|
|
efficiency due to compile-time evaluation of the required conversion path. |
|
|
|
|
*/ |
|
|
|
|
namespace detail { |
|
|
|
|
|
|
|
|
|
// Get C++ string type for specified encoding
|
|
|
|
|
template<Encoding encoding> struct StringTypeSelector; |
|
|
|
|
template<> struct StringTypeSelector<E_ANSI> { typedef std::string Type; }; |
|
|
|
|
template<> struct StringTypeSelector<E_UTF8> { typedef std::string Type; }; |
|
|
|
|
template<> struct StringTypeSelector<E_UTF16> { typedef std::wstring Type; }; |
|
|
|
|
|
|
|
|
|
// Compile-time selection depending on whether a string is managed
|
|
|
|
|
template<typename StringType> struct IfManaged { |
|
|
|
|
struct Select { |
|
|
|
|
template<typename TrueType, typename FalseType> |
|
|
|
|
struct Either { typedef FalseType Type; }; |
|
|
|
|
}; |
|
|
|
|
enum { Result = false }; |
|
|
|
|
}; |
|
|
|
|
template<> struct IfManaged<System::String ^> { |
|
|
|
|
struct Select { |
|
|
|
|
template<typename TrueType, typename FalseType> |
|
|
|
|
struct Either { typedef TrueType Type; }; |
|
|
|
|
}; |
|
|
|
|
enum { Result = true }; |
|
|
|
|
}; |
|
|
|
|
//checking for overflow
|
|
|
|
|
if (_size > INT_MAX) |
|
|
|
|
{ |
|
|
|
|
// this should never happen if _size was returned by GetAnsiStringSize()
|
|
|
|
|
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Direction of the marshaling process
|
|
|
|
|
enum MarshalingDirection { |
|
|
|
|
CxxFromNet, |
|
|
|
|
NetFromCxx |
|
|
|
|
}; |
|
|
|
|
const auto _written = static_cast<size_t>(::WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS, |
|
|
|
|
_pinned_ptr, _str->Length, _buf, static_cast<int>(_size), NULL, NULL)); |
|
|
|
|
if (_written >= _size || (_written == 0 && _size != 1)) // allowing empty string
|
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentException(_EXCEPTION_WC2MB); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
_buf[_written] = '\0'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// The actual marshaling code
|
|
|
|
|
template<MarshalingDirection direction> struct StringMarshaler; |
|
|
|
|
inline void WriteUnicodeStringUTF8Source(_Out_writes_all_(_size) _Post_z_ wchar_t* _dest, size_t _size, _In_reads_bytes_(_count)const char* _src, size_t _count) |
|
|
|
|
{ |
|
|
|
|
//checking for overflow
|
|
|
|
|
if (_size > INT_MAX || _count > INT_MAX) |
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Marshals to .NET from C++ strings
|
|
|
|
|
template<> struct StringMarshaler<NetFromCxx> { |
|
|
|
|
size_t _written = static_cast<size_t>(::MultiByteToWideChar(CP_UTF8, 0, _src, |
|
|
|
|
static_cast<int>(_count), _dest, static_cast<int>(_size))); |
|
|
|
|
if (_written >= _size || (_written == 0 && _size != 1)) // allowing empty string
|
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentException(_EXCEPTION_MB2WC); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template<Encoding encoding, typename SourceType> |
|
|
|
|
static System::String ^marshal(const SourceType &string) { |
|
|
|
|
// Constructs a std::[w]string in case someone gave us a char * to choke on
|
|
|
|
|
return marshalCxxString<encoding, SourceType>(string); |
|
|
|
|
} |
|
|
|
|
_dest[_written] = L'\0'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template<Encoding encoding, typename SourceType> |
|
|
|
|
static System::String ^marshalCxxString( |
|
|
|
|
const typename StringTypeSelector<encoding>::Type &cxxString |
|
|
|
|
) { |
|
|
|
|
typedef typename StringTypeSelector<encoding>::Type SourceStringType; |
|
|
|
|
size_t byteCount = cxxString.length() * sizeof(SourceStringType::value_type); |
|
|
|
|
inline System::String^ InternalUTF8ToStringHelper(_In_reads_z_(_count + 1)const char* _src, size_t _count) |
|
|
|
|
{ |
|
|
|
|
const size_t _size = details::GetUnicodeStringSizeUTF8Source(_src, _count); |
|
|
|
|
if (_size > INT_MAX || _size <= 0) |
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentOutOfRangeException(_EXCEPTION_GREATER_THAN_INT_MAX); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Empty strings would cause trouble accessing the array below
|
|
|
|
|
if(byteCount == 0) { |
|
|
|
|
return System::String::Empty; |
|
|
|
|
} |
|
|
|
|
details::char_buffer<wchar_t> _wchar_buf(_size); |
|
|
|
|
if (_wchar_buf.get() == NULL) |
|
|
|
|
{ |
|
|
|
|
throw gcnew System::InsufficientMemoryException(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Copy the C++ string contents into a managed array of bytes
|
|
|
|
|
cli::array<unsigned char> ^bytes = gcnew cli::array<unsigned char>(byteCount); |
|
|
|
|
{ pin_ptr<unsigned char> pinnedBytes = &bytes[0]; |
|
|
|
|
memcpy(pinnedBytes, cxxString.c_str(), byteCount); |
|
|
|
|
details::WriteUnicodeStringUTF8Source(_wchar_buf.get(), _size, _src, _count); |
|
|
|
|
return gcnew System::String(_wchar_buf.get(), 0, static_cast<int>(_size) - 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Now let one of .NET's encoding classes do the rest
|
|
|
|
|
return decode<encoding>(bytes); |
|
|
|
|
} |
|
|
|
|
template <class _To_Type, class _From_Type> |
|
|
|
|
inline _To_Type marshal_as_utf8(const _From_Type& _from_object); |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
// Converts a byte array based on the selected encoding
|
|
|
|
|
template<Encoding encoding> static System::String ^decode(cli::array<unsigned char> ^bytes); |
|
|
|
|
template<> static System::String ^decode<E_ANSI>(cli::array<unsigned char> ^bytes) { |
|
|
|
|
return System::Text::Encoding::Default->GetString(bytes); |
|
|
|
|
} |
|
|
|
|
template<> static System::String ^decode<E_UTF8>(cli::array<unsigned char> ^bytes) { |
|
|
|
|
return System::Text::Encoding::UTF8->GetString(bytes); |
|
|
|
|
} |
|
|
|
|
template<> static System::String ^decode<E_UTF16>(cli::array<unsigned char> ^bytes) { |
|
|
|
|
return System::Text::Encoding::Unicode->GetString(bytes); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
template <> |
|
|
|
|
inline std::string marshal_as_utf8(System::String^ const& _from_obj) |
|
|
|
|
{ |
|
|
|
|
if (_from_obj == nullptr) |
|
|
|
|
{ |
|
|
|
|
throw gcnew System::ArgumentNullException(_EXCEPTION_NULLPTR); |
|
|
|
|
} |
|
|
|
|
std::string _to_obj; |
|
|
|
|
size_t _size = details::GetUTF8StringSize(_from_obj); |
|
|
|
|
|
|
|
|
|
if (_size > 1) |
|
|
|
|
{ |
|
|
|
|
// -1 because resize will automatically +1 for the NULL
|
|
|
|
|
_to_obj.resize(_size - 1); |
|
|
|
|
char* _dest_buf = &(_to_obj[0]); |
|
|
|
|
|
|
|
|
|
details::WriteUTF8String(_dest_buf, _size, _from_obj); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Marshals to C++ strings from .NET
|
|
|
|
|
template<> struct StringMarshaler<CxxFromNet> { |
|
|
|
|
return _to_obj; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template<Encoding encoding, typename SourceType> |
|
|
|
|
static typename detail::StringTypeSelector<encoding>::Type marshal( |
|
|
|
|
System::String ^string |
|
|
|
|
) { |
|
|
|
|
typedef typename StringTypeSelector<encoding>::Type StringType; |
|
|
|
|
template <> |
|
|
|
|
inline System::String^ marshal_as_utf8(char const* const& _from_obj) |
|
|
|
|
{ |
|
|
|
|
return details::InternalUTF8ToStringHelper(_from_obj, strlen(_from_obj)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Empty strings would cause a problem when accessing the empty managed array
|
|
|
|
|
if(!string || string->Length == 0) { |
|
|
|
|
return StringType(); |
|
|
|
|
template <> |
|
|
|
|
inline System::String^ marshal_as_utf8(const std::string& _from_obj) |
|
|
|
|
{ |
|
|
|
|
return details::InternalUTF8ToStringHelper(_from_obj.c_str(), _from_obj.length()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// First, we use .NET's encoding classes to convert the string into a byte array
|
|
|
|
|
cli::array<unsigned char> ^bytes = encode<encoding>(string); |
|
|
|
|
|
|
|
|
|
// Then we construct our native string from that byte array
|
|
|
|
|
pin_ptr<unsigned char> pinnedBytes(&bytes[0]); |
|
|
|
|
return StringType( |
|
|
|
|
reinterpret_cast<StringType::value_type *>(static_cast<unsigned char *>(pinnedBytes)), |
|
|
|
|
bytes->Length / sizeof(StringType::value_type) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template<> static std::wstring marshal<E_UTF16, System::String ^>( |
|
|
|
|
System::String ^string |
|
|
|
|
) { |
|
|
|
|
// We can directly accesss the characters in the managed string
|
|
|
|
|
pin_ptr<const wchar_t> pinnedChars(::PtrToStringChars(string)); |
|
|
|
|
return std::wstring(pinnedChars, string->Length); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
// Converts a string based on the selected encoding
|
|
|
|
|
template<Encoding encoding> static cli::array<unsigned char> ^encode(System::String ^string); |
|
|
|
|
template<> static cli::array<unsigned char> ^encode<E_ANSI>(System::String ^string) { |
|
|
|
|
return System::Text::Encoding::Default->GetBytes(string); |
|
|
|
|
template <> |
|
|
|
|
inline System::String^ marshal_as_utf8(const std::string_view& _from_obj) |
|
|
|
|
{ |
|
|
|
|
return details::InternalUTF8ToStringHelper(_from_obj.data(), _from_obj.length()); |
|
|
|
|
} |
|
|
|
|
template<> static cli::array<unsigned char> ^encode<E_UTF8>(System::String ^string) { |
|
|
|
|
return System::Text::Encoding::UTF8->GetBytes(string); |
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
inline System::String^ marshal_as(const std::string_view& _from_obj) |
|
|
|
|
{ |
|
|
|
|
return details::InternalAnsiToStringHelper(_from_obj.data(), _from_obj.length()); |
|
|
|
|
} |
|
|
|
|
template<> static cli::array<unsigned char> ^encode<E_UTF16>(System::String ^string) { |
|
|
|
|
return System::Text::Encoding::Unicode->GetBytes(string); |
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
|
inline System::String^ marshal_as(const std::wstring_view& _from_obj) |
|
|
|
|
{ |
|
|
|
|
return details::InternalUnicodeToStringHelper(_from_obj.data(), _from_obj.length()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// CLI extensions namespace
|
|
|
|
|
namespace clix { |
|
|
|
|
|
|
|
|
|
/// <summary>Encoding types for strings</summary>
|
|
|
|
|
enum Encoding { |
|
|
|
|
|
|
|
|
|
/// <summary>ANSI encoding</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is the default encoding you've most likely been using all around in C++. ANSI
|
|
|
|
|
/// means 8 Bit encoding with character codes depending on the system's selected code page.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
E_ANSI, |
|
|
|
|
|
|
|
|
|
/// <summary>UTF-8 encoding</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is the encoding commonly used for multilingual C++ strings. All ASCII characters
|
|
|
|
|
/// (0-127) will be represented as single bytes. Be aware that UTF-8 uses more than one
|
|
|
|
|
/// byte for extended characters, so std::string::length() might not reflect the actual
|
|
|
|
|
/// length of the string in characters if it contains any non-ASCII characters.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
E_UTF8, |
|
|
|
|
|
|
|
|
|
/// <summary>UTF-16 encoding</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is the suggested to be used for marshaling and the native encoding of .NET
|
|
|
|
|
/// strings. It is similar to UTF-8 but uses a minimum of two bytes per character, making
|
|
|
|
|
/// the number of bytes required for a given string better predictable. Be aware, however,
|
|
|
|
|
/// that UTF-16 can still use more than two bytes for a character, so std::wstring::length()
|
|
|
|
|
/// might not reflect the actual length of the string.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
E_UTF16, |
|
|
|
|
E_UNICODE = E_UTF16 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------- //
|
|
|
|
|
// clix::marshalString()
|
|
|
|
|
// ----------------------------------------------------------------------------------------- //
|
|
|
|
|
/// <summary>Marshals strings between .NET managed and C++ native</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This all-in-one function marshals native C++ strings to .NET strings and vice versa.
|
|
|
|
|
/// You have to specify an encoding to use for the conversion, which always applies to the
|
|
|
|
|
/// native C++ string as .NET always uses UTF-16 for its own strings.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <param name="string">String to be marshalled to the other side</param>
|
|
|
|
|
/// <returns>The marshaled representation of the string</returns>
|
|
|
|
|
template<Encoding encoding, typename SourceType> |
|
|
|
|
typename detail::IfManaged<SourceType>::Select::Either< |
|
|
|
|
typename detail::StringTypeSelector<encoding>::Type, |
|
|
|
|
System::String ^ |
|
|
|
|
>::Type marshalString(SourceType string) { |
|
|
|
|
// Ignore this if you're just scanning the headers for informations!
|
|
|
|
|
/* All this template stuff might seem like overkill, but it is well thought out and enables
|
|
|
|
|
you to use a readable and convenient call while still keeping the highest possible code |
|
|
|
|
efficiency due to compile-time evaluation of the required conversion path. |
|
|
|
|
*/ |
|
|
|
|
namespace detail { |
|
|
|
|
|
|
|
|
|
// Get C++ string type for specified encoding
|
|
|
|
|
template<Encoding encoding> struct StringTypeSelector; |
|
|
|
|
template<> struct StringTypeSelector<E_ANSI> { typedef std::string Type; }; |
|
|
|
|
template<> struct StringTypeSelector<E_UTF8> { typedef std::string Type; }; |
|
|
|
|
template<> struct StringTypeSelector<E_UTF16> { typedef std::wstring Type; }; |
|
|
|
|
|
|
|
|
|
// Compile-time selection depending on whether a string is managed
|
|
|
|
|
template<typename StringType> struct IfManaged { |
|
|
|
|
struct Select { |
|
|
|
|
template<typename TrueType, typename FalseType> |
|
|
|
|
struct Either { typedef FalseType Type; }; |
|
|
|
|
}; |
|
|
|
|
enum { Result = false }; |
|
|
|
|
}; |
|
|
|
|
template<> struct IfManaged<System::String ^> { |
|
|
|
|
struct Select { |
|
|
|
|
template<typename TrueType, typename FalseType> |
|
|
|
|
struct Either { typedef TrueType Type; }; |
|
|
|
|
}; |
|
|
|
|
enum { Result = true }; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Direction of the marshaling process
|
|
|
|
|
enum MarshalingDirection { |
|
|
|
|
CxxFromNet, |
|
|
|
|
NetFromCxx |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// The actual marshaling code
|
|
|
|
|
template<MarshalingDirection direction> struct StringMarshaler; |
|
|
|
|
|
|
|
|
|
// Marshals to .NET from C++ strings
|
|
|
|
|
template<> |
|
|
|
|
struct StringMarshaler<NetFromCxx> { |
|
|
|
|
|
|
|
|
|
template<Encoding encoding, typename SourceType> |
|
|
|
|
static System::String ^marshal(const SourceType& string) { |
|
|
|
|
if constexpr (encoding == E_UTF8) |
|
|
|
|
return msclr::interop::marshal_as_utf8<System::String^>(string); |
|
|
|
|
else |
|
|
|
|
return msclr::interop::marshal_as<System::String^>(string); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Marshals to C++ strings from .NET
|
|
|
|
|
template<> |
|
|
|
|
struct StringMarshaler<CxxFromNet> { |
|
|
|
|
template<Encoding encoding, typename SourceType> |
|
|
|
|
static typename detail::StringTypeSelector<encoding>::Type marshal( |
|
|
|
|
System::String^ string |
|
|
|
|
) { |
|
|
|
|
using ResultType = typename detail::StringTypeSelector<encoding>::Type; |
|
|
|
|
return msclr::interop::marshal_as<ResultType>(string); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template<> |
|
|
|
|
static std::string marshal<E_UTF8, System::String^>(System::String^ string) { |
|
|
|
|
return msclr::interop::marshal_as_utf8<std::string>(string); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------- //
|
|
|
|
|
// clix::marshalString()
|
|
|
|
|
// ----------------------------------------------------------------------------------------- //
|
|
|
|
|
/// <summary>Marshals strings between .NET managed and C++ native</summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This all-in-one function marshals native C++ strings to .NET strings and vice versa.
|
|
|
|
|
/// You have to specify an encoding to use for the conversion, which always applies to the
|
|
|
|
|
/// native C++ string as .NET always uses UTF-16 for its own strings.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <param name="string">String to be marshalled to the other side</param>
|
|
|
|
|
/// <returns>The marshaled representation of the string</returns>
|
|
|
|
|
template<Encoding encoding, typename SourceType, class ResultType = |
|
|
|
|
typename detail::IfManaged<SourceType>::Select::Either< |
|
|
|
|
typename detail::StringTypeSelector<encoding>::Type, |
|
|
|
|
System::String^>::Type |
|
|
|
|
> |
|
|
|
|
ResultType marshalString(SourceType string) { |
|
|
|
|
constexpr detail::MarshalingDirection direction = |
|
|
|
|
detail::IfManaged<SourceType>::Result ? detail::CxxFromNet : detail::NetFromCxx; |
|
|
|
|
using StringMarshaler = detail::StringMarshaler<direction>; |
|
|
|
|
|
|
|
|
|
// Pass on the call to our nifty template routines
|
|
|
|
|
return StringMarshaler::template marshal<encoding, SourceType>(string); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} // namespace clix
|
|
|
|
|
|
|
|
|
|
// std::ostream marshaling using a System::IO::TextWriter
|
|
|
|
|
|