summaryrefslogtreecommitdiffstats
path: root/lib/libUPnP/Platinum/Source/Extras/Managed/Clix.h
blob: 2b93f6014c9f1849ebaba8b0d2e21bfd0e618654 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// ------------------------------------------------------------------------------------------- //
// clix.h
//
// http://www.nuclex.org/articles/5-cxx/10-marshaling-strings-in-cxx-cli
//
// Marshals strings between .NET and C++ using C++/CLI (Visual C++ 2005 and later only).
// Faster and cleaner than the System::Interop method because it uses garbage collected memory.
// Use at your own leisure. No warranties whatsoever provided.
//
// Original code by Markus Ewald (http://www.nuclex.org/articles/marshaling-strings-in-cxx-cli)
// Updated version including several improvements suggested by Neil Hunt
// ------------------------------------------------------------------------------------------- //
#pragma once
 
#include <string>
#include <vcclr.h>
 
// 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

  };

  // 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 StringTypeSelecter;
    template<> struct StringTypeSelecter<E_ANSI> { typedef std::string Type; };
    template<> struct StringTypeSelecter<E_UTF8> { typedef std::string Type; };
    template<> struct StringTypeSelecter<E_UTF16> { typedef std::wstring Type; };

    // Compile-time check whether a given type is a managed System::String
    template<typename StringType> struct IsManagedString { enum { Result = false }; };
    template<> struct IsManagedString<System::String ^> { enum { Result = true }; };

    // Compile-time selection of two types depending on a boolean expression
    template<bool expression> struct Select;
    template<> struct Select<false> {
      template<typename TrueType, typename FalseType> struct Type { typedef FalseType Result; };
    };
    template<> struct Select<true> {
      template<typename TrueType, typename FalseType> struct Type { typedef TrueType Result; };
    };

    // 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) {
        // Constructs a std::[w]string in case someone gave us a char * to choke on
        return marshalCxxString<encoding, SourceType>(string);
      }
      
      template<Encoding encoding, typename SourceType>
      static System::String ^marshalCxxString(
        const typename StringTypeSelecter<encoding>::Type &cxxString
      ) {
        typedef typename StringTypeSelecter<encoding>::Type SourceStringType;
        size_t byteCount = cxxString.length() * sizeof(SourceStringType::value_type);

        // Copy the C++ string contents into a managed array of bytes
        array<unsigned char> ^bytes = gcnew array<unsigned char>(byteCount);
        { pin_ptr<unsigned char> pinnedBytes = &bytes[0];
          memcpy(pinnedBytes, cxxString.c_str(), byteCount);
        }

        // Now let one of .NET's encoding classes do the rest
        return decode<encoding>(bytes);
      }

      private:
        // Converts a byte array based on the selected encoding
        template<Encoding encoding> static System::String ^decode(array<unsigned char> ^bytes);
        template<> static System::String ^decode<E_ANSI>(array<unsigned char> ^bytes) {
          return System::Text::Encoding::Default->GetString(bytes);
        }
        template<> static System::String ^decode<E_UTF8>(array<unsigned char> ^bytes) {
          return System::Text::Encoding::UTF8->GetString(bytes);
        }
        template<> static System::String ^decode<E_UTF16>(array<unsigned char> ^bytes) {
          return System::Text::Encoding::Unicode->GetString(bytes);
        }
    };

    // Marshals to C++ strings from .NET
    template<> struct StringMarshaler<CxxFromNet> {

      template<Encoding encoding, typename SourceType>
      static typename detail::StringTypeSelecter<encoding>::Type marshal(
        System::String ^string
      ) {
        typedef typename StringTypeSelecter<encoding>::Type StringType;

        // First, we use .NET's encoding classes to convert the string into a byte array
        array<unsigned char> ^bytes = encode<encoding>(string);

        // fix crash if empty string passed
        if (bytes->Length == 0) return StringType();

        // 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
      ) {
        // fix crash if empty string passed
        if (string->Length == 0) return std::wstring();

        // We can directly access 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 array<unsigned char> ^encode(System::String ^string);
        template<> static array<unsigned char> ^encode<E_ANSI>(System::String ^string) {
          return System::Text::Encoding::Default->GetBytes(string);
        }
        template<> static array<unsigned char> ^encode<E_UTF8>(System::String ^string) {
          return System::Text::Encoding::UTF8->GetBytes(string);
        }
        template<> static array<unsigned char> ^encode<E_UTF16>(System::String ^string) {
          return System::Text::Encoding::Unicode->GetBytes(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>
  typename detail::Select<detail::IsManagedString<SourceType>::Result>::Type<
    typename detail::StringTypeSelecter<encoding>::Type,
    System::String ^
  >::Result marshalString(SourceType string) {
   
    // Pass on the call to our nifty template routines
    return detail::StringMarshaler<
      detail::IsManagedString<SourceType>::Result ? detail::CxxFromNet : detail::NetFromCxx
    >::marshal<encoding, SourceType>(string);
   
  }

} // namespace clix