Reputation: 63
I am currently using Visual Studio 2010. This is my first time writing a wrapper for a C++ class. The class in C++ looks like this:
bool exampleCode(char* arrayOfStrings[], int number, char* regularString)
I created the header file which looks like:
bool exampleCode(array<String^>^ arrayOfStrings, int number, String^ regularString)
and the .cpp file class looks like:
bool exampleCode(array<String^>^ arrayOfStrings, int number, System::String^ regularString)
I figured out how to marshal the regularString data but I'm not sure how to convert the String
array into a char*[]
. Any help would be appreciated.
Upvotes: 0
Views: 1818
Reputation: 283684
Here's an easily reusable version (complete with demonstration) that improves on @ildjarn's answer by reducing the number of allocations and improving locality. It also uses your choice of encoding such as 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;
}
Upvotes: 0
Reputation: 62975
The following is ideally efficient (minimal copying) and exception-safe:
#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());
}
(Note that number
does not need to be passed in to the managed function as it can be deduced from the array's length.)
Alternatively, incorporating Ben Voigt's suggestion to use std::string
instead of the Marshal
class:
#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, ®[0]);
}
Upvotes: 1