Hej gruppe
Jeg har et problem med et C++-program som bruger et C-bibliotek.
Biblioteket ser i princippet sådan her ud:
$ cat lib.h
// lib.h
// Dette skal forestille et "eksternt" bibliotek
#ifndef _LIB_H_
#define _LIB_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int a;
int b;
} LIB_CTX;
LIB_CTX *lib_make_ctx (void);
void lib_free_ctx (LIB_CTX *ctx);
int lib_ctx_set_a (LIB_CTX *ctx, int a); // Return non-zero for error
int lib_ctx_set_b (LIB_CTX *ctx, int b); // Return non-zero for error
void lib_use_ctx (LIB_CTX *ctx);
#ifdef __cplusplus
}
#endif
#endif // _LIB_H_
$ cat lib.c
// lib.c
// Dette skal forestille et "eksternt" bibliotek
#include "lib.h"
#include <stdlib.h>
#include <stdio.h>
LIB_CTX *lib_make_ctx (void)
{
LIB_CTX *ctx = malloc (sizeof (LIB_CTX));
if (!ctx)
return 0;
ctx->a = 0;
ctx->b = 0;
return ctx;
}
void lib_free_ctx (LIB_CTX *ctx)
{
free (ctx);
}
int lib_ctx_set_a (LIB_CTX *ctx, int a)
{
if (a > 5)
return 1;
ctx->a = a;
return 0;
}
int lib_ctx_set_b (LIB_CTX *ctx, int b)
{
if (b > 7)
return 1;
ctx->b = b;
return 0;
}
void lib_use_ctx (LIB_CTX *ctx)
{
printf ("Use of LIB_CTX %p\n", ctx);
}
Jeg vil gerne undgå at arbejde med pointere til LIB_CTX, så jeg har
lavet en klasse MY_CTX som stærkt inspireret af autoptr gerne skulle
sørge for at kalde lib_free_ctx() når min pointer går ud af scope:
$ cat make_ctx.h
// make_ctx.h
#ifndef _MAKE_CTX_H_
#define _MAKE_CTX_H_
#include "lib.h"
class MY_CTX
{
private:
LIB_CTX *ctx;
public:
MY_CTX (LIB_CTX *c);
MY_CTX (MY_CTX &c);
~MY_CTX ();
MY_CTX & operator = (MY_CTX &c);
operator LIB_CTX * () const;
bool operator ! () const;
};
MY_CTX make_ctx ();
#endif // _MAKE_CTX_H_
$ cat make_ctx.cpp
// make_ctx.cpp
#include "make_ctx.h"
#include <iostream>
using std::cout;
using std::endl;
MY_CTX::MY_CTX (LIB_CTX *c)
{
ctx = c;
cout << "constructor - ctx = " << ctx << endl;
}
MY_CTX::MY_CTX (MY_CTX &c)
{
ctx = c.ctx;
c.ctx = 0;
cout << "copy constructor - ctx = " << ctx << endl;
}
MY_CTX:
MY_CTX ()
{
cout << "destructor - ctx = " << ctx << endl;
if (ctx)
lib_free_ctx (ctx);
}
MY_CTX & MY_CTX:
erator = (MY_CTX &c)
{
cout << "assignment - ctx = " << ctx << " RHS.ctx = " << c.ctx << endl;
if (this != &c)
{
if (ctx)
lib_free_ctx (ctx);
ctx = c.ctx;
c.ctx = 0;
}
return *this;
}
MY_CTX:
erator LIB_CTX * () const
{
cout << "konvertering til LIB_CTX * - ctx = " << ctx << endl;
return ctx;
}
bool MY_CTX:
erator ! () const
{
cout << "operator ! - ctx = " << ctx << endl;
return ! ctx;
}
MY_CTX make_ctx ()
{
MY_CTX ctx (lib_make_ctx ());
if (!ctx)
throw "Cannot make ctx";
if (lib_ctx_set_a (ctx, 3))
throw "Cannot set a";
if (lib_ctx_set_b (ctx, 4))
throw "Cannot set b";
return ctx;
}
Endelig har jeg et lille testprogram:
$ cat main.cpp
// main.cpp
#include "make_ctx.h"
int main ()
{
MY_CTX ctx (make_ctx ());
lib_use_ctx (ctx);
return 0;
}
Når programmet køres, sker der følgende:
$ cat Makefile
# Makefile
prog: main.o make_ctx.o lib.o
g++ -Wall -W main.o make_ctx.o lib.o -o prog
main.o: main.cpp make_ctx.h lib.h
gcc -Wall -W -c main.cpp -o main.o
make_ctx.o: make_ctx.cpp make_ctx.h lib.h
gcc -Wall -W -c make_ctx.cpp -o make_ctx.o
lib.o: lib.c lib.h
gcc -Wall -W -c lib.c -o lib.o
$ make
gcc -Wall -W -c main.cpp -o main.o
gcc -Wall -W -c make_ctx.cpp -o make_ctx.o
gcc -Wall -W -c lib.c -o lib.o
g++ -Wall -W main.o make_ctx.o lib.o -o prog
$ ./prog
constructor - ctx = 0x804a538
operator ! - ctx = 0x804a538
konvertering til LIB_CTX * - ctx = 0x804a538
konvertering til LIB_CTX * - ctx = 0x804a538
konvertering til LIB_CTX * - ctx = 0x804a538
constructor - ctx = 0x804a538
destructor - ctx = 0x804a538
konvertering til LIB_CTX * - ctx = 0x804a538
Use of LIB_CTX 0x804a538
destructor - ctx = 0x804a538
Jeg havde forventet at copy-constructoren for MY_CTX blev brugt når
returværdien skal foreføres fra make_ctx() til main(), men i stedet
konverteres værdien til en LIB_CTX* med det resultat at lib_free_ctx()
kaldes fra make_ctx() med den samme pointer som også gives til main()
med katastrofale konsekvenser til følge.
Hvorfor virker programmet ikke? Og hvordan får jeg det til at virke så
lib_free_ctx() ikke kaldes fra make_ctx() hvis pointeren bliver overført
til main()?
På forhånd tak og mange hilsener
Byrial