2012-01-11 16 views
0

私は現在Visual Studio 2010を使用しています。これは初めてのC++クラスのラッパーの作成です。 C++でのクラスは、次のようになります。C#の文字列[]をC++のChar * []に変換します

bool exampleCode(char* arrayOfStrings[], int number, char* regularString) 

私はのように見えるヘッダファイル作成:

bool exampleCode(array<String^>^ arrayOfStrings, int number, String^ regularString) 

と.cppファイルのクラスのようになります。私は考え出し

bool exampleCode(array<String^>^ arrayOfStrings, int number, System::String^ regularString) 

をどのようにregularStringデータをマーシャリングするのですが、String配列をchar*[]に変換する方法がわかりません。どんな助けもありがとう。

+0

ネイティブ関数は、渡されたメモリをクリーンアップするか、それを呼び出し元に残しますか? – ildjarn

+0

私はネイティブ関数が渡されたメモリを消去すると信じていますが、私はC++/CLIプログラミングの初心者ですから100%ではありません。 – avtoader

+0

さて、次にネイティブ関数がメモリをクリーンアップするのですか? 'フリー'? 'delete []'? 'CoTaskMemFree'? 'LocalFree'? 'GlobalFree'? – ildjarn

答えて

0

ここでは、割り当ての数を減らし、地域を改善することで、@ ildjarnの答えを改善する、簡単に再利用可能なバージョン(デモンストレーションが完成しました)です。また、UTF-8などのエンコーディングを選択して使用します。

#include <iostream> 
#include <vector> 

#include <windows.h> 
#include <vcclr.h> 

using namespace System; 

template <typename T> 
struct advanced_marshal; 

template <> 
struct advanced_marshal<char*[]> 
{ 
    char** get() { return &m_strings[0]; } 

    advanced_marshal(array<System::String^>^ strings, UINT code_page = CP_ACP) : m_strings(strings->Length) 
    { 
     if (int count = strings->Length) { 
      int i; 
      size_t total_length_estimate = count; // one NUL byte per string 
      for(i = 0; i < count; ++i) { 
       total_length_estimate += strings[i]->Length * 4; 
      } 

      m_buffer.resize(total_length_estimate); 
      auto tail = m_buffer.begin(), end = m_buffer.end(); 
      i = 0; 
      do { 
       m_strings[i] = &*tail; 
       pin_ptr<const WCHAR> pwsz = PtrToStringChars(strings[i]); 
       tail += 1 + WideCharToMultiByte(code_page, 0, pwsz, strings[i]->Length, &*tail , end - tail, nullptr, nullptr); 

       ++i; 
      } while (i < count); 
     } 
    } 

    advanced_marshal(advanced_marshal<char*[]>&& other) { m_buffer.swap(other.m_buffer); m_strings.swap(other.m_strings); } 

private: 
    advanced_marshal(const advanced_marshal<char*[]>&); // = delete 
    void operator=(const advanced_marshal<char*[]>&); // = delete 

    std::vector<char> m_buffer; 
    std::vector<char*> m_strings; 
}; 

void print_some_strings(char* strings[], int num) 
{ 
    for(int i = 0; i < num; ++i) 
     std::cout << strings[i] << "\n"; 

    std::cin.get(); 
} 

int main(array<System::String ^> ^args) 
{ 
    print_some_strings(advanced_marshal<char*[]>(args).get(), args->Length); 
    return 0; 
} 
1

次は、(最小限のコピー)理想的に効率的であり、例外セーフ:

#include <algorithm> 
#include <memory> 
#include <vector> 

using System::IntPtr; 
using System::String; 
using System::Runtime::InteropServices::Marshal; 

bool exampleCodeManaged(array<String^>^ arrayOfStrings, String^ regularString) 
{ 
    auto deleter = [](char* p) { Marshal::FreeHGlobal(IntPtr(p)); }; 
    typedef std::unique_ptr<char[], decltype(deleter)> cstr_t; 
    auto make_cstr = [&deleter](String^ s) 
    { 
     return cstr_t(
      static_cast<char*>(Marshal::StringToHGlobalAnsi(s).ToPointer()), 
      deleter 
     ); 
    }; 

    std::vector<cstr_t> cstrs; 
    cstrs.reserve(arrayOfStrings->Length); 
    for each (String^ s in arrayOfStrings) 
     cstrs.push_back(make_cstr(s)); 

    std::vector<char*> ptrs; 
    ptrs.reserve(cstrs.size()); 
    std::for_each(
     cstrs.begin(), 
     cstrs.end(), 
     [&ptrs](cstr_t& cstr) { ptrs.push_back(cstr.get()); } 
    ); 

    auto reg_cstr = make_cstr(regularString); 

    return exampleCode(ptrs.data(), arrayOfStrings->Length, reg_cstr.get()); 
} 

は(それが配列のから推定することができるようnumberが管理関数に渡される必要はないことに注意してください長)の代わりにMarshalクラスのstd::stringを使用するベンフォークトの提案を組み込む代わり

、:。

#include <algorithm> 
#include <string> 
#include <vector> 
#include <msclr/marshal_cppstd.h> 

using System::String; 

bool exampleCodeManaged(array<String^>^ arrayOfStrings, String^ regularString) 
{ 
    using msclr::interop::marshal_as; 

    std::vector<std::string> strs; 
    strs.reserve(arrayOfStrings->Length); 
    for each (String^ s in arrayOfStrings) 
     strs.push_back(marshal_as<std::string>(s)); 

    std::vector<char*> ptrs; 
    ptrs.reserve(strs.size()); 
    std::for_each(
     strs.begin(), 
     strs.end(), 
     [&ptrs](std::string& s) { ptrs.push_back(&s[0]); } 
    ); 

    auto reg = marshal_as<std::string>(regularString); 

    return exampleCode(ptrs.data(), arrayOfStrings->Length, &reg[0]); 
} 
+0

「StringToHGlobalAnsi」が「理想的には効率的」であるかどうかはわかりません。実際には 'HGLOBAL'を生成せず、結果を' GlobalFree'に渡すことはできません(これは ' HGLOBAL's)。 –

+0

@Ben:どちらかとは分かりませんが、 'marshal_as 'や 'marshal_as 'を使っていました。これは、 'const_cast <>'が使われていない限り、データの余分なコピーが必要です。私はむしろ 'const_cast <>'を避けたいと考えています。 – ildjarn

+0

'std :: string'は完全に使えます。ちょうど'&s [0] 'です。しかし、私は '' Encoding :: GetBytes'(http://msdn.microsoft.com/en-us/library/6s1x2atd)を呼んで、単一のベクトルを好んでいると思います。aspx)(すべての文字列をエンドツーエンドでパックします)。 1つの割り振り、自動空き、カスタム・デリゲーターなし。 –