Using C++ / Objective-C package (Hunspell) function in Swift with char*** argument - c++

I'm a recent Swift learner and now I'm trying to use following package:
https://github.com/aaronSig/Hunspell-iOS
The package itself is written using C++ and has some headers for Objective-C. I've added *-Bridging-Header.h file and have been able to use the package inside Swift code, but now I don't understand how to use it properly:
The comments for Objective-C headers for the library say following:
/* suggest(suggestions, word) - search suggestions
* input: pointer to an array of strings pointer and the (bad) word
* array of strings pointer (here *slst) may not be initialized
* output: number of suggestions in string array, and suggestions in
* a newly allocated array of strings (*slts will be NULL when number
* of suggestion equals 0.)
*/
LIBHUNSPELL_DLL_EXPORTED int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word);
But I don't know how to get correct pointer now. I've tried to utilize answers from here:
How to pass an array of Swift strings to a C function taking a char ** parameter
But this is where I got so far

The problem here is that the function parameters expect a pointer type, not an array, and you have to provide arguments correspondingly. Assuming you have a function declared like this:
#ifdef __cplusplus
extern "C" {
#endif
int foo(char*** inoutStrArr, const char* prop);
#ifdef __cplusplus
}
#endif
And defined something like this:
int foo(char*** inoutStrArr, const char* prop) {
*inoutStrArr = new char*[] {
"one", "two", "three", "four"
};
return 4;
}
It wants you to allocate a single pointer of type char*** only as an argument:
// A `char***` pointer
let inoutPtr = UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?>.allocate(capacity: 1)
// result contains number of elements
let result = Int(foo(inoutPtr, "test"))
// checks whether the function assigned anything to the passed in pointer
guard let array = inoutPtr.pointee else {
print("No result")
return
}
// Array of utf8 strings
let cStrings = (0 ..< result).compactMap { array[$0] }
// Array of swift strings
let strings = cStrings.map { String.init(utf8String: $0) }

Related

Array type 'char [n]' and 'uint8 [n]' is not assignable

I'm trying to convert two types of values: char and uint8.
I have a function someFunction(TLabel *label, TEdit *edit). Inside of this function, I have a variable char var[6+1].
What I'm trying to do:
Get an Alphanumeric text (input of TEdit *edit), convert this text and put it inside of the var.
I know that if I call this function and put the *label and the *edit I can get whatever I want, but the problem is the conversion to associate the text of *edit in the var.
An example of my code (inside of the function):
char var[6+1];
label->Text = "Some text";
var = edit->Text;
//I will put var value inside of an another char (like a #define) that is in a struct, but doesn't matter for now
my_struct.valueOfVar = var;
And I have another function, it's the same code but the valueOfVar above is a uint8, and I can't convert it, too:
uint8 valueOfAnotherVar[6+1];
The issue is not with the types of the elements in the arrays, but rather that you simply can't assign one static array to another like this:
char a[7];
uint8 b[7];
a = b; // error
Instead, if you want to copy the values in the arrays, you can use std::copy like this:
std::copy(std::begin(b), std::end(b), std::begin(a));
If you have a dynamic array, you can still use std::copy, but you have to use the size of the array:
std::copy(b, b + N, a);
Make sure that both a and b point to at least N elements, otherwise you'll invoke undefined behavior.
The Text of a TLabel or TEdit is a System::String, which is an alias for System::AnsiString in C++Builder 2007 and earlier, and for System::UnicodeString in C++Builder 2009 and later. You did not say which version you are using, but it makes a big difference in your example.
You can't assign anything directly to a fixed array, like char[7] or uint8[7], by using operator= like you are attempting to do. You will need to instead convert and copy the String data into the allocated memory of the arrays, eg:
char var[6+1] = {};
label->Text = "Some text"; // OK - String has a constructor for 'const char*'
// if using CB2007 or earlier:
strncpy(var, edit->Text.c_str(), 6);
// if using CB2009 or later:
strncpy(var, AnsiString(edit->Text).c_str(), 6);
And then you can use one of these to copy the contents of var into valueOfVar:
memcpy(my_struct.valueOfVar, var, 7);
std::copy(var, var+7, my_struct.valueOfVar);
std::copy_n(var, 7, my_struct.valueOfVar);

Ctype in python

i have this C++ code in linux ubuntu i want use this method in python by ctype
but can not send parameter to ctype.cdl.funcion
C++ code :
extern "C" unsigned char* getuserdata(int code,unsigned char* globalkey,unsigned char* key)
{
unsigned char data[256];
KeyA *keya;
keya=new KeyA;
keya->OpenDevice(0);
keya->Init(globalkey,globalkey,globalkey,globalkey);
keya->ReadUserMemory( 0,256,key,data);
return data;
}
sample use this function in C++:
unsigned char g_user[16] = { 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22 };
unsigned char publickey[16] = { 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55 };
printf("function Return: %s\n", getuserdata(0,publickey,g_user));
and my python source code (not Worked!!) is :
from ctypes import *
dl=cdll.LoadLibrary('/home/iman/KCore/kcore/hkey.so');
dl.getuserdata.restype = c_char_p
string_buffers = [addressof(create_string_buffer(16)) ]
string_buffers[0]= "5555555555555555";
string_buffers2 = [addressof(create_string_buffer(16)) ]
string_buffers2[0]="2222222222222222";
st= dl.getuserdata(0,string_buffers,string_buffers2);
print st+"\n";
Let's look at the code...
string_buffers = [addressof(create_string_buffer(16)) ]
This line creates a Python list containing the address of a 16-byte string buffer (or maybe it's not bytes but characters, please find that out yourself).
string_buffers[0]= "5555555555555555";
This line replaces the pointer from above with the string "555555555555555".
dl.getuserdata(0,string_buffers,string_buffers2);
Here, you pass the list with a string to the function, while the function takes a pointer to bytes. Question is what you want to achieve here, i.e. whether you want the buffer to be written to or not. If not, use const in C++ and simply pass "22222" as parameter, ctypes will do the rest for you automatically.
That said, it could be that I'm guessing wrong, since you haven't explained what exactly is happening (quote the error messages!) and how exactly you determined that something doesn't work. Further, you should clean up your broken C++ code or temporarily replace it with something smaller that is more suitable to explain the exact problem.

Passing 2-D array with dynamic size between functions in C/++

This is "popular" question so I already checked the similar threads but still didnt resolve my issue.
How can I declare 2-D array to hold "strings" - I need array of array of chars AFAIK - and use it as argument to 5 functions one after another where I can pass it by reference and update the content dynamically so following function can compare it. (I might get 10 "strings" or even empty array, so I want to do it correctly with dynamic array coz array content is different from system to system).
"string" => C style string aka array of chars. MAXLEN < 32;
C solution would be more disirable but if vectors can work, why not.
One possible solution in C is as follows:
char **p_strings = calloc(num_strings, sizeof(*p_strings));
for (i = 0; i < num_strings; i++)
{
// Allocate storage for the i-th string (always leave room for '\0')
p_strings[i] = calloc(len_string[i]+1, sizeof(*p_strings[i]));
}
...
// Call a function
my_function(p_strings, num_strings);
You will need to remember to free all this data when you're done with it.
If you need to alter the length of a string, or change the number of strings, you will have to do some fairly painful reallocation. So if you're working in C++, you should probably just be using a std::vector<std::string>.
std::vector<std::string> strings;
strings.push_back("Foo");
strings.push_back("Bar");
...
my_function(strings);
You can even get const pointers to C-style strings for each element, using c_str().
Assuming C++; for this I see no problem with using a vector to string (the string serves as the second dimension):
void foo(vector<string> v) {
cout << v[0]; // Assuming the elements exist!
}
int main(int argc, char *argv[])
{
vector<string> vString; // Make the vector
vString.push_back("something"); // Add a string
foo(vString); // Print out 'something'
}
In your edit you also described that the only thing that will change would be the actual string, so instead of push_backing your strings when they are needed, you can init the vector with the length:
vector<string> vString(10); // Assuming a size of 10
and then use them normally:
vString[4] = "something";
and (in response to the comment), to resize at runtime:
vString.resize(15); // Make it bigger, generates new blank strings

Beginner C++ Question

I have followed the code example here
toupper c++ example
And implemented it in my own code as follows
void CharString::MakeUpper()
{
char* str[strlen(m_pString)];
int i=0;
str[strlen(m_pString)]=m_pString;
char* c;
while (str[i])
{
c=str[i];
putchar (toupper(c));
i++;
}
}
But this gives me the following compiler error
CharString.cpp: In member function 'void CharString::MakeUpper()':
CharString.cpp:276: error: invalid conversion from 'char*' to 'int'
CharString.cpp:276: error: initializing argument 1of 'int toupper(int)'
CharString.cpp: In member function 'void CharString::MakeLower()':
This is line 276
putchar (toupper(c));
I understand that toupper is looking for int as a parameter and returns an int also, is that the problem? If so how does the example work?
Also,
char* str[strlen(m_pString)];
int i=0;
str[strlen(m_pString)]=m_pString;
is not valid C++ - arrays must be dimensioned using compile time constants - this is a C99 feature. And I really don't think the code would do what you want it to, even if it were legal, as you seem to be accessing one past the end of the array. It would be handy if you posted the complete class definition.
I don't think your code does what you want it to do and in fact if it compiled it would explode.
char* str[strlen(m_pString)]; // you've made an array of X C strings where
// X is the length of your original string.
int i=0;
str[strlen(m_pString)]=m_pString; // You've attempted to assign the C string in your array
// at location X to point at you m_pString. X is the
// same X as before and so is 1 past the end of the array
// This is a buffer overrun.
I think what you actually wanted to do was to copy the content of m_pString into str. You'd do that like so:
char * str = new char[strlen(m_pString)];
memcpy(str, m_pString); // I may have the operands reversed, see the docs.
The easier way to do this though is to stop using C strings and to use C++ strings:
std::string str = m_pString;
There are more issues, but this should get you steer you more toward the right direction.
You need to feed toupper() an int (or a char) instead of a char *, which is how you've declared c.
try:
char c;
Also,
char* str[strlen(m_pString)];
is an an array of pointers to characters, not just a single string.
This line:
str[strlen(m_pString)]=m_pString;
is an assignment to a bad pointer then, since there was no allocation.
I'm going to go with the assumption that m_pString is a C style string (char *). You're doing way more fiddling than you need to be doing.
void CharString::MakeUpper()
{
char* str = m_pString; // Since you're not modifying the string, there's no need to make a local copy, just get a pointer to the existing string.
while (*str) // You can use the string pointer as an iterator over the individual chars
{
putchar (toupper(*str)); // Dereference the pointer to get each char.
str++; // Move to the next char (you can merge this into the previous line if so desired, but there's no need.
}
}
In the example you cite, the reason it works is because of how the variables are declared.
int main ()
{
int i=0;
char str[]="Test String.\n"; // This is a compile time string literal, so it's ok to initialize the array with it. Also, it's an array of `char`s not `char*`s.
char c; // Note that this is also a `char`, not a `char *`
while (str[i])
{
c=str[i];
putchar (toupper(c));
i++;
}
return 0;
}
Because of the error-prone ways of using C strings, your best bet is std::string:
void CharString::MakeUpper()
{
string str(m_pString);
transform(str.begin(), str.end(), ostream_iterator<char>(cout), &toupper);
}
There is not a built-in conversion from char * to int, which is why the error occurs. Since you're trying to capitalize a character, you need to dereference the pointer.
putchar(toupper(*c));

Dealing with char arrays in C++

I have this C-styled piece of initialization code:
const char * const vlc_args[] =
{
"-I", "dummy",
"--ignore-config",
"--extraintf=logger",
"--verbose=2"
"--plugin-path=/usr/lib/vlc"
};
//tricky calculation of the char space used
libvlc_new(sizeof(vlc_args)/sizeof(vlc_args[0]), vlc_args, &exc);
Since I need to make the --plugin-path parameter dynamic, I can't use a static array anymore. So I came up with a C++ alternative:
std::string pluginpath = "test";
libvlc_exception_t exc;
std::vector<std::string> args;
args.push_back("-I");
args.push_back("dummy");
args.push_back("--ignore-config");
args.push_back("--extraintf=logger");
args.push_back("--verbose=2");
args.push_back("--ipv4");
args.push_back("--plugin-path=" + pluginpath);
std::string combinedString;
for (size_t idx = 0; idx < args.size(); ++idx)
{
combinedString.append(args[idx]);
combinedString.resize(combinedString.size() + 1);
combinedString[combinedString.size() - 1] = 0;
}
combinedString.resize(combinedString.size() + 1);
combinedString[combinedString.size() - 1] = 0;
size_t size = combinedString.size();
const char * data = combinedString.c_str();
libvlc_new(size, &data, &exc); // => error occurs here (not at end of scope or anything)
But this results in a segmentation fault. So there must be an error in my code, which I can't seem to find.. Can anyone spot it?
Solved!
Thanks to Joseph Grahn and Jason Orendorff. My idea on the memory layout of the C-style array was wrong. I thought all data was organized as a big sequential block. In reality it's a list of pointers to the first character of each individual string.
This code works:
std::vector<const char*> charArgs;
for (size_t idx = 0; idx < args.size(); ++idx)
{
charArgs.push_back(&(args[idx][0]));
}
mVLCInstance = libvlc_new(charArgs.size(),
&charArgs[0],
&mVLCException);
I think Josef Grahn is right: the API wants an actual array of pointers.
If you don't need to add arguments programmatically, you can just go back to using an array:
std::string pluginpath = "test";
std::string pluginpath_arg = "--plugin-path=" + pluginpath;
const char *args[] = {
"-I", dummy, "--ignore-config", ..., pluginpath_arg.c_str()
};
libvlc_exception_t exc;
libvlc_new(sizeof(args) / sizeof(args[0]), args, &exc);
EDIT: There might also be a problem with using c_str() here. This is true if VLC keeps the pointer and uses it again later; I can't tell if that's the case from the docs.
You are appending all arguments into a single string, then you pass a pointer to the const char * string to libvlc_new as if it were an array of char *.
(I'm not sure this is the problem, but it seems a bit strange.)
Have you tried:
libvlc_new(size, data, &exc);
instead of
libvlc_new(size, &data, &exc);
It seems you use the null bytes to make the string act like an array of characters, but then you pass a pointer to the char* "array" instead of just the array.
The library call expects a pointer to an array of const char* (that is several pointers), but you pass it a single pointer. That more characters got appended to the end of that string doesn't matter.
To dynamically build an array of the required pointers you could use another vector:
// store c_str() pointers in vector
std::vector<const char*> combined;
for (size_t idx = 0; idx < args.size(); ++idx) {
combined.push_back(args[idx].c_str());
}
// pass pointer to begin of array (it is guaranteed that the contents of a vector
// are stored in a continuous memory area)
libvlc_new(combined.size(), &(combined[0]), &exc);
Jason Orendorff's remark is also valid here: This will not work if libvlc_new stores the passed pointer internally for later use.
Regarding the segmentation violation
No solution, as there will probably be more problems
You are sending only 1 string in. (not sure if it is allowed by libvlc_new) So the first parameter should be set to 1, ie size = 1. I believe this will solve the segmentation problem. But I doubt libvlc_new can be called with just one line of multiple parameters.
In the original code sizeof(vlc_args)/sizeof(vlc_args[0]) will have the number of parameters as entries in the vector. In your example equal 6.
Your code
size_t size = combinedString.size(); // a long string, size >> 1
const char * data = combinedString.c_str(); // data is a pointer to the string
libvlc_new(size, &data, &exc);
// size should be 1 because data is like an array with only one string.
// &data is the adress to the "array" so to speak. If size > 1 the function
// will try to read pass the only string available in this "array"
I think Jason Orendorff has a good solution to fix it all...
I had this same issue; I wanted to dynamicly generate the arguments, but in a safe c++ way.
The solution I hit on was to use the unique_ptr<[]> new to C++ 11. For example:
unique_ptr<char *[]> vlc_argv;
int vlc_argc;
...
auto vlc(libvlc_new(vlc_argc, vlc_argv.get()));
This gives me a nice RAII object to hold my arguments in that I can still pass into libvlc_new(). Since the arguments are raw pointers in argv, managed by the OS, we can just use those in our vlc_argv directly.
As a further example assume I process the first few args out of argv (somewhere in the "..." area above), and want to pass everything from next_arg on to libvlc_new():
vlc_argv = std::unique_ptr<char *[]>(new char *[2 + argc - next_arg]);
vlc_argv[0] = argv[0];
for (vlc_argc=1; vlc_argc <= argc - next_arg; vlc_argc++) {
vlc_argv[vlc_argc] = argv[next_arg + vlc_argc - 1];
}
vlc_argv[vlc_argc] = 0;
The problem is due to the use of c_str() and storing the pointer.
See stringstream, string, and char* conversion confusion
EDIT Forget what I said... it was late... see the comments :)