Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C++] Functiepointer naar member functie laten verwijzen

Pagina: 1
Acties:

  • Hmail
  • Registratie: April 2003
  • Laatst online: 10:05

Hmail

Doet ook maar wat.

Topicstarter
Ik raak de weg inmiddels een beetje kwijt. Ik ben geen professioneel C++ programmeur, maar ik kan er toch aardig mijn weg in vinden. Maar nu wil ik sqlite in mijn applicatie gaan gebruiken, en ik krijg het niet gecompileerd. Kan iemand me helpen?

Volgens de documentatie moet ik de volgende code gebruiken om een query uit te voeren:
C++:
1
2
3
4
5
6
7
int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluted */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);


De quickstart geeft een eenvoudig C voorbeeldje:
C:
1
2
3
4
static int callback(void *NotUsed, int argc, char **argv, char **azColName){
// ...
}
rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);


En zo werkt het bij mij ook. Echter, ik werk met C++, en ik wil alles in een object wegwerken. Alleen daar loop ik vast:
(Mijn probleem zit hem in de callbackfunctie, die wil ik erbij hebben. Als ik dat als 0 aanroep compileert het wel)
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class db {
private:
    sqlite3 *cldb;
public:
    bool query(string);
    int __cdecl select_callback(void *p_data, int num_fields, char **p_fields, char **p_col_names);
};
int __cdecl db::select_callback(void *p_data, int num_fields, char **p_fields, char **p_col_names) {
    return 0;
}
bool db::query(string q) {
    char *zErr = 0;
    static int (__cdecl db::*fp)(void*, int, char**, char**);
    fp = &db::select_callback;
    if(sqlite3_exec(cldb, q.c_str(), fp, 0, &zErr) == SQLITE_OK) {
        return true;
    } else {
        return false;
    }
}


De __cdecl code heb ik erbij gezet omdat Visual Studio klaagde dat hij geen int(__thiscall db::*) naar int(__cdecl *) kon compileren, maar heel veel effect heeft het niet gehad:
code:
1
cannot convert from 'int (__cdecl db::* )(void *,int,char **,char **)' to 'int (__cdecl *)(void *,int,char **,char **)

En dat gaat dan uiteraard om regel 15 van het laatste stukje code. Weet iemand hoe ik ervoor zorg dat sqlite3_exec ook naar een member-function-pointer kan verwijzen? Ik kom er met Google niet uit, deze post leek nog wat te helpen, maar na het toevoegen van de function pointer kreeg ik dus nog steeds dezelfde foutmelding. Iemand?

It might sound as if I have no clue what I'm doing, but I actually have a vague idea.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:02
Er zijn eigenlijk twee problemen:
• SQLite gebruik een C calling conventie, en geen "thiscall" conventie
• Een member functie heeft een this-pointer als impliciet argument.

Het gevolg is dat je eigenlijk een C++ member functie niet als C functie aan SQLite mee kunt geven. (Je kunt waarschijnlijk wel wat hacken, maar netjes is anders.)

Een goede manier om dit op te lossen is een gewone callback functie schrijven en die je methode te laten aanroepen. Nadeel is dat je maar één argument hebt om je context door te geven en je gebruikt die ook bij je member functie. Dat kun je oplossen door een tijdelijke struct te gebruiken waar je this-pointer én het eerste argument van de "echte" callback in zit.

Zoiets wordt het dan:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class db {
// ..
int select_callback(void *p_data, int num_fields, char **p_fields, char **p_colnames);
};

struct select_callback_context
{
    db *db;
    void *p_data;
};

int select_callback_thunk(void *ctx, int num_fields,
                          char **p_fields, char **p_col_names)
{
    select_callback_context *context = (select_callback_context *)ctx;
    return context->db->select_callback(context->p_data, num_fields, p_fields, p_colnames);
}

bool db::query(string q) {
    // ..
    select_callback_context context = { this, p_data };
    sqlite3_exec( /* .. */ &select_callback_thunk, &context /*..*/ );
    // etc.
}


Als je echte callback geen extra data nodig heeft (zoals in je voorbeeld) dan kun je het gedoe met de extra struct in principe achterwege laten (want dan kun je gewoon de this-pointer als argument naar de callback thunk meegeven).

Als je veel verschillende callbacks nodig hebt, is het verder waarschijnlijk handig om de struct uit te breiden met een pointer naar een member functie, zodat je één callback thunk kunt gebruiken voor al je C++ callback functies.

  • Hmail
  • Registratie: April 2003
  • Laatst online: 10:05

Hmail

Doet ook maar wat.

Topicstarter
Hmm, ik had gehoopt dat het volledig in hetzelfde object had weggewerkt kunnen worden, maar dit is ook wel een minder mooie, maar goede oplossing :)

Thanks!

It might sound as if I have no clue what I'm doing, but I actually have a vague idea.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 16-11 18:33
Je kunt de callback zelf binnen de class houden door em static te maken.

[ Voor 8% gewijzigd door farlane op 23-03-2008 20:16 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • redfox314
  • Registratie: December 2004
  • Laatst online: 07-11 14:35
en er een functor van maken door de operator() te overloaden in je doelobject?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dan kun je 'm uiteraard nog altijd niet casten naar een C functie, omdat operator() niets anders is dan een normale member functie.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1