2

I'm using a library which requires a function with a void* pointer as a parameter. I have a 2D string array and I want to pass that array through that parameter and extract it inside the function. I successfully passed the array as a pointer but I don't know how to convert that pointer back to my array.

This is my current code:

String str_array[100][10];

int callback(void* data) {

  String* str_array_ptr[100][10] = (String* [100][10])data;

  (*str_array_ptr)[0][0] = "text";

  return 0;

}

void test() {
  callback(&str_array);
}

However, when compiling, I obtain the following error message:

error: ISO C++ forbids casting to an array type 'String* [100][10]' [-fpermissive]

PS: I'm trying to use the SQLite library's sqlite3_exec() function and store the result of a "SELECT SQL query" into a 2D string array.

SQLite C Interface - One-Step Query Execution Interface

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ManWithNoName
  • 381
  • 1
  • 6
  • 20
  • I'm using Arduino IDE and we can mix C and CPP files. – ManWithNoName Jun 02 '23 at 06:35
  • 1
    There is no such thing as C/CPP. `String` is not a standard C++ type. In C++ we avoid C-style arrays, raw pointers, void pointers, and casts. `String* [100][10]` is a 100x10 array of pointers to `String`. If you need a pointer to an array, the best option is to use `typedef`. – n. m. could be an AI Jun 02 '23 at 06:41
  • @Jason There is nothing in either of those so-called 'duplicates' about **casting** to a 2D array pointer. I shall reopen this question... – Adrian Mole Jun 02 '23 at 06:56
  • @AdrianMole This has nothing to do with casting. OP just doesn't know the proper way to create a pointer to 2d array. – Jason Jun 02 '23 at 06:59
  • 2
    @AdrianMole Just including "casting" in the title of the question doesn't make the question about casting. – Jason Jun 02 '23 at 07:00
  • @Jason What do you mean, nothing to do with casting? Read the first word of the title ... and don't assume what the OP may or may not know. – Adrian Mole Jun 02 '23 at 07:01
  • 2
    @AdrianMole There is nothing to assume here. Just because the title contains the word "casting" doesn't mean the question has to do something with casting or that the problem is related to casting. The duplicates solves OP's problem. – Jason Jun 02 '23 at 07:02
  • Moreover, more accurate title would be "Forming a pointer to 2D array" – Jason Jun 02 '23 at 07:06
  • What exactly should happen when the code runs? What should be in `str_array` to begin with? What is `String`, and what do you expect to be the element type? Furthermore, if you know that the code will pass a specific array type to the function, and have written the function such that its logic expects that array type, **why use `void *` as a parameter type**? – Karl Knechtel Jun 02 '23 at 07:19
  • @Karl Knechtel I'm using a sqlite library which need a function with a void pointer in param. I have found examples with primitive types but not with array. – ManWithNoName Jun 02 '23 at 07:33
  • After adding OP explanations from the comments, the question post seems to be pretty clear. Voted to reopen. – Tsyvarev Jun 02 '23 at 09:49
  • SQLite and Arduino are normally ***never*** in the same sentence (or project). What kind of system is it? – Peter Mortensen Jun 03 '23 at 23:28
  • It is best to avoid String altogether (due to the dynamic memory allocation), at least with a microcontroller with only 2 KB of RAM. – Peter Mortensen Jun 03 '23 at 23:30
  • @PeterMortensen I'm using Espressif ESP32 SoC series. SQLite is used in a mini WebServer application. (about 400 KB of SRAM for the model I'm using) – ManWithNoName Jun 08 '23 at 21:01

2 Answers2

5

You cannot cast a pointer to an array. Instead you access your array through another pointer. That pointer has type String (*)[10]. Like this

String str_array[100][10];

int callback(void* data) { 

    String (*str_array_ptr)[10] = (String (*)[10])data;

    str_array_ptr[0][0] = "text"; // Note no '*'

    return 0;

}

void test() {
    callback(str_array); // Note no '&'
}

Both the way you create the pointer, you don't need to use &, and the way you access the pointer, you don't need to use *, are also wrong in your code. See the code above for details.

The fundamental issue here (and maybe the issue you are misunderstanding) is the difference between String *x[10]; and String (*x)[10];. In the first case x is an array of 10 pointers to String, in the second case x is a pointer to an array of ten String. It's the second option that you want.

john
  • 85,011
  • 4
  • 57
  • 81
  • You're right, I confused an "array of pointers" and a "pointer to an array" syntax. Maybe I need to sleep a bit more ! Thanks – ManWithNoName Jun 02 '23 at 07:21
  • Just one *minor* nit-pick: This question is tagged as C++, so why the C-style cast? – Adrian Mole Jun 02 '23 at 14:17
  • @AdrianMole This is an issue, I updated it. The sqlite library I'm using is well in C language. I'm working on Arduino projects and there are cpp and c files libraries. Sometimes, I get mixed up a bit. – ManWithNoName Jun 03 '23 at 10:20
  • @ManWithNoName You changed the language tag *after* answers have been posted - thus invalidating my answer. Also, your cited error message is still (quite clearly) from a C++ (not C) compiler. And the Arduino compiler (IIRC) works as a **C++** compiler. – Adrian Mole Jun 03 '23 at 10:46
  • @AdrianMole OK, thanks for the info. Arduino IDE is a bit a mystery for me. Before, I worked on MPLAB IDE in ASM and C. Having the possibility to work in C++ with object on microcontrollers is new for me ! – ManWithNoName Jun 03 '23 at 12:40
  • @ManWithNoName I used a C style cast in this answer simply because that was what you used in your code. Aside from the `String` type (which I know nothing about) this answer should work well as C or C++. – john Jun 03 '23 at 12:41
  • @john This is exactly that I want ! the sqlite library I'm using is coded in C. But using C and C++ in the same time is not always easy for me. It is right using String type is maybe not the best way in this case. Maybe because I use to use Object with OOP language instead primitive type. – ManWithNoName Jun 03 '23 at 13:06
  • The library I'm using : "siara-cc / esp32_arduino_sqlite3_lib" Example file : "sqlite3_littlefs / sqlite3_littlefs.ino" – ManWithNoName Jun 03 '23 at 13:18
  • @ManWithNoName I don't know anything specifically about the libraries you are using. But in general there is no reason that a C library cannot be used from C++ code. So my advice would be to write all of your code in C++, this should not prevent you using sqllite. C++ classes like `std::string` and `std::vector` have methods that allow you to interface them with C code, maybe the same is true of `String` although as I said I know nothing specifically about that. – john Jun 03 '23 at 14:47
  • @john By talking about vector, I remember I can't use it on Arduino IDE but I just found one library adapted for the micro-controller usage (**janelia-arduino / Vector**). I have to try it. In my usage, I need to get SQL SELECT results in a 2D string array. But because I can't know in advance the array size (it depends of the sql request and the result set size), I must define an array with enough space and check if there are values or not in each square of the array. – ManWithNoName Jun 03 '23 at 16:34
2

The line, String* str_array_ptr[100][10]; does not declare a pointer to a 2D array of String objects; rather, it declares a 2D array of pointers to String objects.

The syntax for declaring a pointer to a 2D array is tricky; in your case, it is the following (to declare str_array_ptr as a pointer to a 100 x 10 array of String objects):

String (*str_array_ptr)[100][10];

Casting to such a pointer is (arguably) even trickier; using a C++ static_cast (which you can when the source operand is a void*), you get the following code:

#include <iostream>
#include <string>
using String = std::string;

String str_array[100][10];

int callback(void* data) {

    String (*str_array_ptr)[100][10] = static_cast<String (*)[100][10]>(data);
    (*str_array_ptr)[0][0] = "text";
    return 0;
}

void test() {
    callback(&str_array);
}

One could argue that, if you really wish to use such a pointer to a 2D array (there are likely better design solutions in C++, such as the answer posted by john), then you could define a type for that pointer with a typedef or using... statement (e.g. using StrArrPtrType = decltype(&str_array);); however, the C++ and C programming communities generally frown (severely and rightly) on hiding pointers behind typedef or using aliases.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Although the OP says that he wants a pointer to a 2D array, and that's what you have above. I think that's likely a misunderstanding on the OPs part. Probably they just want some code that works, and there is an extra level of indirection than is necessary in the code above. – john Jun 02 '23 at 06:56
  • @john Yes, indeed. I did originally mean to reference your answer in the "better design solutions" part of my post (which I have now done). – Adrian Mole Jun 02 '23 at 07:00