LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 01-20-2011, 01:57 PM   #1
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
Calling C++ from "C" (-friendly) code - am I doing it right ?


I know it's a FAQ, and I read info from the WEB as well as related portions of C++ standard.

In this case I insist on compiling the final "C" part by "C" (and not C++) compiler - this is because I am trying to understand in detail how I can create bindings of something originally written in C++ for something which is written in "C" and has a well defined interface for foreign functions written in "C", but not in C++.

The code I am presenting works fine for me; I want to know whether it works correctly by construction (of course, this is my goal) or by chance.


The example code consists of:

'Point2D.hxx' file:

Code:
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n Point2D.hxx
     1  #include <iostream>
     2
     3  #include "DATA_TYPE.h"
     4
     5  #define INLINE_CXX inline
     6
     7  class Point2D
     8    {
     9    private:
    10      DATA_TYPE x;
    11      DATA_TYPE y;
    12
    13    public:
    14       INLINE_CXX Point2D()
    15         {
    16         std::cerr << "Point2D() called\n";
    17         }
    18
    19       INLINE_CXX Point2D(DATA_TYPE x, DATA_TYPE y)
    20         {
    21         std::cerr << "Point2D(" << x << ", " << y << ") called\n";
    22         this->x = x;
    23         this->y = y;
    24         }
    25
    26       INLINE_CXX Point2D(const Point2D &other)
    27         {
    28         std::cerr << "Point2D(const Point2D &other) called\n";
    29         this->x = other.x;
    30         this->y = other.y;
    31         }
    32
    33       INLINE_CXX void set(DATA_TYPE x, DATA_TYPE y)
    34         {
    35         std::cerr << "set(" << x << ", " << y << ") called\n";
    36         this->x = x;
    37         this->y = y;
    38         }
    39
    40       INLINE_CXX DATA_TYPE get_x()
    41         {
    42         std::cerr << "get_x() called\n";
    43         return x;
    44         }
    45
    46       INLINE_CXX DATA_TYPE get_y()
    47         {
    48         std::cerr << "get_y() called\n";
    49         return y;
    50         }
    51
    52       INLINE_CXX ~Point2D()
    53         {
    54          std::cerr << "~Point2D() called\n";
    55         }
    56    };
    57
sergei@amdam2:~/junk/c++_work/calling_c++_from_c>
,

'Point2D_C_interface.h' file - this file contains prototypes for the intermediate "C" functions calling C++ methods:

Code:
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n Point2D_C_interface.h
     1  #ifndef POINT2D_C_INTERFACE__DOT__H_INCLUDED
     2  #define POINT2D_C_INTERFACE__DOT__H_INCLUDED
     3
     4  #include "DATA_TYPE.h"
     5
     6  EXTERN_C INLINE_C void *Point2D_allocator_void();
     7  EXTERN_C INLINE_C void *Point2D_allocator_x_y(DATA_TYPE x, DATA_TYPE y);
     8
     9  EXTERN_C INLINE_C DATA_TYPE Point2D_get_x(void * const obj);
    10  EXTERN_C INLINE_C DATA_TYPE Point2D_get_y(void * const obj);
    11
    12  EXTERN_C INLINE_C void Point2D_copy_constructor(void * self, void * const other);
    13
    14  EXTERN_C INLINE_C void Point2D_deallocator(void * const obj);
    15
    16  #endif
sergei@amdam2:~/junk/c++_work/calling_c++_from_c>
,

'Point2D_C_interface.c' file - this is the implementation of the above "C" functions:

Code:
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n Point2D_C_interface.c
     1  /*
     2  compile with:
     3  ~/AFSWD/install/gcc-4.4.5/binsh/g++ -O3 -Wall -Wextra -c Point2D_C_interface.c -o Point2D_C_interface.o
     4  */
     5
     6  #include "Point2D.hxx"
     7
     8  #define EXTERN_C extern "C"
     9  #define INLINE_C
    10  #include "Point2D_C_interface.h"
    11
    12  INLINE_C void *Point2D_allocator_void()
    13    {
    14    Point2D *obj = new Point2D();
    15    return obj;
    16    }
    17
    18  INLINE_C void *Point2D_allocator_x_y(DATA_TYPE x, DATA_TYPE y)
    19    {
    20    Point2D *obj = new Point2D(x, y);
    21    return obj;
    22    }
    23
    24
    25  // static_cast is unsafe, but I want the cast to happen at compile time
    26  // and this code is to be called from the language bindings it's
    27  // intended for, so the actual object should be of correct type/layout
    28  INLINE_C DATA_TYPE Point2D_get_x(void * const obj)
    29    {
    30    return static_cast<Point2D * const>(obj)->get_x();
    31    }
    32
    33  INLINE_C DATA_TYPE Point2D_get_y(void * const obj)
    34    {
    35    return static_cast<Point2D * const>(obj)->get_y();
    36    }
    37
    38  INLINE_C void Point2D_copy_constructor(void * self, void * const other)
    39    {
    40    std::cerr << "Point2D_copy_constructor(void * self, void * other) called\n";
    41    Point2D t = *(static_cast<Point2D *>(other));
    42    std::cerr << "just after assigning to 't'\n";
    43    *(static_cast<Point2D *>(self)) = t;
    44    std::cerr << "returning from Point2D_copy_constructor(void * self, void * const other)\n";
    45    }
    46
    47  INLINE_C void Point2D_deallocator(void * const obj)
    48    {
    49    delete static_cast<Point2D * const>(obj);
    50    }
    51
    52
    53
sergei@amdam2:~/junk/c++_work/calling_c++_from_c>
,

'main.c' - a simple test program checking how it works together:

Code:
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n main.c
     1  /*
     2  compile and link with:
     3  ~/AFSWD/install/gcc-4.4.5/binsh/gcc -O3 -Wall -Wextra main.c Point2D_C_interface.o -o main -lstdc++
     4  */
     5
     6
     7  #include <stdio.h>
     8  #include <stdlib.h>
     9
    10  #define EXTERN_C extern
    11  #define INLINE_C inline
    12  #include "Point2D_C_interface.h"
    13
    14  int main()
    15    {
    16    void *p1 = Point2D_allocator_void();
    17    if(NULL ==  p1)
    18      {
    19      fprintf(stderr, ":ERROR: couldn't allocate memory for p1\n");
    20      exit(EXIT_FAILURE);
    21      }
    22
    23    fprintf(stderr, "(can be random junk) Point2D_get_x(p1) : %g Point2D_get_y(p1) : %g\n", Point2D_get_x(p1), Point2D_get_y(p1));
    24
    25
    26    void *p2 = Point2D_allocator_x_y(1.23, 4.56);
    27    if(NULL ==  p2)
    28      {
    29      fprintf(stderr, ":ERROR: couldn't allocate memory for p2\n");
    30      exit(EXIT_FAILURE);
    31      }
    32
    33    fprintf(stderr, "Point2D_get_x(p2): %g\n", Point2D_get_x(p2));
    34
    35    Point2D_copy_constructor(p1, p2);
    36
    37    fprintf(stderr, "Point2D_get_x(p1) : %g Point2D_get_y(p1) : %g\n", Point2D_get_x(p1), Point2D_get_y(p1));
    38
    39    Point2D_deallocator(p1);
    40    Point2D_deallocator(p2);
    41
    42    return EXIT_SUCCESS;
    43    }
sergei@amdam2:~/junk/c++_work/calling_c++_from_c>
,

and 'DATA_TYPE.h' file (no, I don't care about templates at the moment) :

Code:
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n DATA_TYPE.h
     1  #ifndef DATA_TYPE__DOT__H_INCLUDED
     2  #define DATA_TYPE__DOT__H_INCLUDED
     3
     4  #define DATA_TYPE double
     5
     6  #endif
sergei@amdam2:~/junk/c++_work/calling_c++_from_c>
.

The whole thing is compiled and linked by the following command lines:

Code:
~/AFSWD/install/gcc-4.4.5/binsh/g++ -O3 -Wall -Wextra -c Point2D_C_interface.c -o Point2D_C_interface.o
~/AFSWD/install/gcc-4.4.5/binsh/gcc -O3 -Wall -Wextra main.c Point2D_C_interface.o -o main -lstdc++
and it is run this way:

Code:
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> ./main
Point2D() called
get_y() called
get_x() called
(can be random junk) Point2D_get_x(p1) : 0 Point2D_get_y(p1) : 0
Point2D(1.23, 4.56) called
get_x() called
Point2D_get_x(p2): 1.23
Point2D_copy_constructor(void * self, void * other) called
Point2D(const Point2D &other) called
just after assigning to 't'
returning from Point2D_copy_constructor(void * self, void * const other)
~Point2D() called
get_y() called
get_x() called
Point2D_get_x(p1) : 1.23 Point2D_get_y(p1) : 4.56
~Point2D() called
~Point2D() called
sergei@amdam2:~/junk/c++_work/calling_c++_from_c>
.

So, if something is wrong with this, please tell me.

Thanks in advance,
Sergei.

P.S. Probably a better jobs with const-ness can be done - I don't yet know how, and likely it's not critical for the final goal of creating bindings.
 
Old 01-20-2011, 03:21 PM   #2
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190
Quote:
Originally Posted by Sergei Steshenko View Post
I want to know whether it works correctly by construction (of course, this is my goal) or by chance.
I didn't spot anything that looked to me like it worked by "chance" (meaning worked by non guaranteed implementation details of your current tool set). But your code is ugly enough I can't be sure.

What are you trying to accomplish with the defines INLINE_CXX and INLINE_C ? Those don't seem to make sense, especially the latter, which doesn't look like it is or ever should be inline.

Quote:
'Point2D_C_interface.c' file - this is the implementation of the above "C" functions:
I'm bothered by that C++ source file having the extension .c and also by your terminology. The definitions for extern "C" declarations are C++ functions with C interfaces.

I also strongly prefer reinterpret_cast for converting C++ class pointers to/from C void*
In this usage, it means exactly the same to the compiler as static_cast but reinterpret_cast is correctly descriptive of the actual operation and thus more meaningful to any human reading the code.

I also think it is better to define EXTERN_C inside the interface.h using a conditional that directly checks the language (C vs. C++) that the compiler is compiling at the moment. Making the .c or .cpp file that includes the interface.h file responsible for that step is a much less maintainable design. At the moment I forget the format of the #if that tests whether the compiler is currently compiling C++, but I think that should be easy to look up.
 
Old 01-20-2011, 08:06 PM   #3
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
Quote:
Originally Posted by johnsfine View Post
I also think it is better to define EXTERN_C inside the interface.h using a conditional that directly checks the language (C vs. C++) that the compiler is compiling at the moment. Making the .c or .cpp file that includes the interface.h file responsible for that step is a much less maintainable design. At the moment I forget the format of the #if that tests whether the compiler is currently compiling C++, but I think that should be easy to look up.
I agree.
Code:
#ifndef POINT2D_C_INTERFACE__DOT__H_INCLUDED
#define POINT2D_C_INTERFACE__DOT__H_INCLUDED

#ifdef __cplusplus
#define EXTERN_C extern "C"
#else
#define EXTERN_C extern
#endif

...

#endif
 
Old 01-21-2011, 07:03 AM   #4
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Original Poster
Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
Guys, thanks for the feedback. Actually, I do not like my own mapping of copy-constructor, and I'm going change it and to add some more stuff.

Meanwhile I'd like to better understand why 'reinterpret_cast' is more appropriate than 'static_cast'. Both are described on pages 74 .. 76 of 'C++ Standard - ANSI ISO IEC 14882 2003.pdf': http://openassist.googlecode.com/fil...2%25202003.pdf
.

I chose 'static_cast' because it has (as I understand it) less functionality, i.e., if I haven't missed something, it does not perform pointer <-> conversions. I.e. my general approach was to choose something suitable with the least possible amount of degrees of freedom.
 
Old 01-21-2011, 09:29 AM   #5
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Original Poster
Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
More on casts: http://stackoverflow.com/questions/3...t-cast-be-used :

Quote:
reinterpret_cast is the most dangerous cast, and should be used very sparingly. It turns one type directly into another - such as casting the value from one pointer to another, or storing a pointer in an int, or all sorts of other nasty things. Largely, the only guarantee you get with reinterpret_cast is that if you cast the result back to the original type, you will get the same value. Other than that, you're on your own. reinterpret_cast cannot do all sorts of conversions; in fact it is relatively limited. It should almost never be used (even interfacing with C code using void* can be done with static_cast).
- guys, I really want to hear why reinterpret_cast is better. You, of course, may say the above quote is just another personal opinion, but I'd like to hear real arguments in favor/against.
 
Old 01-21-2011, 09:45 AM   #6
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
I have no comments on whether to use reinterpret_cast is better/worse than static_cast. I have used the former countless times, and never encountered a "nasty" or "dangerous" experience. It is my understanding that the static_cast is the equivalent of the C-style cast.

IMHO, if you are working on a complex project, determining whether to use reinterpret_cast vs. static_cast should be the least of your worries.
 
Old 01-21-2011, 10:00 AM   #7
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190
Quote:
Originally Posted by Sergei Steshenko View Post
- guys, I really want to hear why reinterpret_cast is better. You, of course, may say the above quote is just another personal opinion, but I'd like to hear real arguments in favor/against.
The above quote is just a personal opinion. My own opinion is also just a personal opinion.

In practice a static_cast to or from a void* does exactly the same thing as a reinterpret_cast to or from a void*.

Deep aside comment: If I understand the standard correctly, the above is true only in practice, not in theory. Those two cast methods are defined by the standard to each do the same thing (restore the original pointer) if the same one (static vs. reinterpret) is used both to void* and from void*. But the standard does not seem to say you could safely use one to go to void* and the other to go back. In practice you can.

My opinion on which is better is based on what they actually do:

When casting between pointers other than void*, a static_cast checks significant aspects of the possible correctness of the cast and adjusts the pointer correctly for the difference between base and derived class. reinterpret_cast does none of that extra work for you. It just reinterprets the pointer as the requested kind of pointer.

When casting to/from void* both static_cast and reinterpret_cast act exactly the same way only reinterpret_cast acts for non void pointers. They both do no type checking and just reinterpret the pointer. My opinion is that it is better to make that fact more obvious to any human reading your code.
 
Old 01-21-2011, 11:04 AM   #8
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Original Poster
Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
OK, I've managed to get rid of type casts - by cheating/"cheating" in the way I'm dealing with function prototypes.

I'm now working on other aspects, I'll publish the new code.
 
Old 01-22-2011, 12:00 AM   #9
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
Quote:
Originally Posted by Sergei Steshenko View Post
In this case I insist on compiling the final "C" part by "C" (and not C++) compiler - this is because I am trying to understand in detail how I can create bindings of something originally written in C++ for something which is written in "C" and has a well defined interface for foreign functions written in "C", but not in C++.
In general, you'll need to link the C++ portion with g++ because it will sometimes create link-time dependencies with hidden object files or static libraries, not just libstdc++. If you want it to be truly C compatible you'll have to create a shared library to pre-link all the g++ dependencies, which isn't a big deal, and it certainly isn't a big hassle.
Quote:
Originally Posted by Sergei Steshenko View Post
- guys, I really want to hear why reinterpret_cast is better. You, of course, may say the above quote is just another personal opinion, but I'd like to hear real arguments in favor/against.
As far as I know, reinterpret_cast does absolutely nothing but give you a waiver to use a pointer however you want to, i.e. a traditional C-cast. This usually isn't a problem if you're casting to the first base class, provided it's not virtual, or if you're casting back to the same class that a void pointer was cast from. In contrast, static_cast is often not necessary unless you're casting to a derived class or to avoid ambiguity with operator overloads; implicit casting takes care of almost everything.

You actually don't need a cast if you declare struct Point2D; in your C-header within extern "C" (you don't actually define the structure in C, though.) In fact, that would be the appropriate way to handle this situation rather than void*.
Kevin Barry
 
Old 01-22-2011, 04:05 AM   #10
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Original Poster
Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
Quote:
Originally Posted by ta0kira View Post
...
You actually don't need a cast if you declare struct Point2D; in your C-header within extern "C" (you don't actually define the structure in C, though.) In fact, that would be the appropriate way to handle this situation rather than void*.
Kevin Barry
Thanks for the tip. My solution at the moment is this:


Code:
#ifdef __cplusplus
#define ObjType Point2D *
#else
#define ObjType void *
#endif
and I'm using ObjType as type in, say:

Code:
INLINE_C void Point2D_set(ObjType const obj, const DATA_TYPE x, const DATA_TYPE y)
  {
  obj->set(x, y);
  }
. The point also is that I do need 'void *' in some cases in "C" - when I preallocate one way or another memory for the object. The preallocated object isn't of type Point2D :

Code:
INLINE_C void Point2D_cast_to_this_void(void *obj)
  {
  Point2D *t = new Point2D();
  *(static_cast<Point2D * const>(obj)) = *t;
  }
...
// in 'main':
  char on_stack_obj[SIZEOF_Point2D];
  Point2D_cast_to_this_void(on_stack_obj);
  Point2D_set(on_stack_obj, 9.87, 6.54);
  fprintf(stderr, "Point2D_get_x(on_stack_obj) : %g Point2D_get_y(on_stack_obj) : %g\n", Point2D_get_x(on_stack_obj), Point2D_get_y(on_stack_obj));
  Point2D_destruct(on_stack_obj);

But if to take your idea further and to really implement somehow (WRT to size, i.e. number of bytes in it) Point2D in "C", e.g.:

Code:
typedef char Point2D[SIZEOF_Point2D];
and then use such Point2D or cast to it (say, after 'malloc'), then I won't need 'void *' - I think I'll try it.
 
Old 01-22-2011, 11:01 AM   #11
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Original Poster
Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
OK, here is the new version:

Code:
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n Point2D.hxx
     1  #include <iostream>
     2
     3  #include "DATA_TYPE.h"
     4
     5  #define INLINE_CXX inline
     6
     7  class Point2D
     8    {
     9    private:
    10      DATA_TYPE x;
    11      DATA_TYPE y;
    12
    13    public:
    14       INLINE_CXX Point2D()
    15         {
    16         std::cerr << "Point2D() called\n";
    17         }
    18
    19       INLINE_CXX Point2D(const DATA_TYPE x, const DATA_TYPE y)
    20         {
    21         std::cerr << "Point2D(" << x << ", " << y << ") called\n";
    22         this->x = x;
    23         this->y = y;
    24         }
    25
    26       INLINE_CXX Point2D(const Point2D &other)
    27         {
    28         std::cerr << "Point2D(const Point2D &other) called\n";
    29         this->x = other.x;
    30         this->y = other.y;
    31         }
    32
    33       INLINE_CXX void set(const DATA_TYPE x, const DATA_TYPE y)
    34         {
    35         std::cerr << "set(" << x << ", " << y << ") called\n";
    36         this->x = x;
    37         this->y = y;
    38         }
    39
    40       INLINE_CXX DATA_TYPE get_x()
    41         {
    42         std::cerr << "get_x() called\n";
    43         return x;
    44         }
    45
    46       INLINE_CXX DATA_TYPE get_y()
    47         {
    48         std::cerr << "get_y() called\n";
    49         return y;
    50         }
    51
    52       INLINE_CXX ~Point2D()
    53         {
    54          std::cerr << "~Point2D() called\n";
    55         }
    56    };
    57
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n Point2D_C_interface.h
     1  #ifndef POINT2D_C_INTERFACE__DOT__H_INCLUDED
     2  #define POINT2D_C_INTERFACE__DOT__H_INCLUDED
     3
     4  #ifdef __cplusplus
     5  #include <cstring>
     6  #define ObjType Point2D *
     7  #else
     8  #include <stddef.h>
     9  typedef char Point2D[16]; // '16' is known from earlier Point2D_sizeof() call in a test program
    10  #define ObjType Point2D
    11  #endif
    12
    13  #include "DATA_TYPE.h"
    14
    15  EXTERN_C INLINE_C void Point2D_cast_to_this(ObjType obj);
    16  EXTERN_C INLINE_C size_t Point2D_sizeof();
    17  EXTERN_C INLINE_C Point2D *Point2D_allocate();
    18  EXTERN_C INLINE_C Point2D *Point2D_allocate_and_initilaize_with_x_y(const DATA_TYPE x, const DATA_TYPE y);
    19  EXTERN_C INLINE_C void Point2D_set(ObjType const obj, const DATA_TYPE x, const DATA_TYPE y);
    20
    21  EXTERN_C INLINE_C DATA_TYPE Point2D_get_x(ObjType const obj);
    22  EXTERN_C INLINE_C DATA_TYPE Point2D_get_y(ObjType obj);
    23
    24  EXTERN_C INLINE_C Point2D *Point2D_clone(ObjType const obj);
    25
    26  EXTERN_C INLINE_C void Point2D_destruct(ObjType obj);
    27  EXTERN_C INLINE_C void Point2D_deallocate(ObjType obj);
    28
    29  #endif
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n Point2D_C_interface.cxx
     1  /*
     2  compile with:
     3  ~/AFSWD/install/gcc-4.4.5/binsh/g++ -O3 -Wall -Wextra -c Point2D_C_interface.cxx -o Point2D_C_interface.o
     4  */
     5
     6  #include "Point2D.hxx"
     7  #include <typeinfo>
     8
     9  #define EXTERN_C extern "C"
    10  #define INLINE_C
    11  #include "Point2D_C_interface.h"
    12
    13  EXTERN_C INLINE_C size_t Point2D_sizeof()
    14    {
    15    return sizeof(Point2D);
    16    }
    17
    18  INLINE_C void Point2D_cast_to_this(ObjType obj)
    19    {
    20    Point2D t(0.0, 0.0); // any constructor call is good here - just to ensure basic initialization
    21                         // in order to be able to call methods later.
    22                         // care should be taken to make sure automatically called destructor of 't'
    23                         // doesn't cause problems.
    24    *obj = t;
    25    }
    26
    27  INLINE_C Point2D *Point2D_allocate()
    28    {
    29    Point2D *obj = new Point2D();
    30    return obj;
    31    }
    32
    33  INLINE_C Point2D *Point2D_allocate_and_initilaize_with_x_y(const DATA_TYPE x, const DATA_TYPE y)
    34    {
    35    Point2D *obj = new Point2D(x, y);
    36    return obj;
    37    }
    38
    39  INLINE_C void Point2D_set(ObjType const obj, const DATA_TYPE x, const DATA_TYPE y)
    40    {
    41    obj->set(x, y);
    42    }
    43
    44  INLINE_C DATA_TYPE Point2D_get_x(ObjType const obj)
    45    {
    46    return obj->get_x();
    47    }
    48
    49  INLINE_C DATA_TYPE Point2D_get_y(ObjType const obj)
    50    {
    51    return obj->get_y();
    52    }
    53
    54  INLINE_C Point2D *Point2D_clone(ObjType const obj)
    55    {
    56    Point2D *t = new Point2D(*obj);
    57    return t;
    58    }
    59
    60  INLINE_C void Point2D_destruct(ObjType obj)
    61    {
    62    obj->~Point2D();
    63    }
    64
    65  INLINE_C void Point2D_deallocate(ObjType obj)
    66    {
    67    delete obj;
    68    }
    69
    70
    71
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> cat -n main.c
     1  /*
     2  compile and link with:
     3  ~/AFSWD/install/gcc-4.4.5/binsh/gcc -O3 -Wall -Wextra main.c Point2D_C_interface.o -o main -lstdc++
     4  */
     5
     6
     7  #include <stdio.h>
     8  #include <stdlib.h>
     9
    10  #define EXTERN_C extern
    11  #define INLINE_C inline
    12  #include "Point2D_C_interface.h"
    13
    14  int main()
    15    {
    16    fprintf(stderr, "sizeof(Point2D)=%llu\n\n", (unsigned long long)Point2D_sizeof());
    17
    18
    19    fprintf(stderr, "BEGIN on_stack_obj\n");
    20
    21    Point2D on_stack_obj;
    22    Point2D_cast_to_this(on_stack_obj);
    23    Point2D_set(on_stack_obj, 9.87, 6.54);
    24    fprintf(stderr, "Point2D_get_x(on_stack_obj): %g Point2D_get_y(on_stack_obj): %g\n", Point2D_get_x(on_stack_obj), Point2D_get_y(on_stack_obj));
    25    Point2D_destruct(on_stack_obj);
    26
    27    fprintf(stderr, "END   on_stack_obj\n\n");
    28
    29
    30
    31    fprintf(stderr, "BEGIN p1\n");
    32
    33    void *p1 = Point2D_allocate();
    34    if(NULL ==  p1)
    35      {
    36      fprintf(stderr, ":ERROR: couldn't allocate memory for p1\n");
    37      exit(EXIT_FAILURE);
    38      }
    39
    40    fprintf(stderr, "(can be random junk) Point2D_get_x(p1): %g Point2D_get_y(p1): %g\n", Point2D_get_x(p1), Point2D_get_y(p1));
    41
    42    Point2D_deallocate(p1);
    43
    44    fprintf(stderr, "END   p1\n\n");
    45
    46
    47    fprintf(stderr, "BEGIN p2\n");
    48
    49    void *p2 = Point2D_allocate_and_initilaize_with_x_y(1.23, 4.56);
    50    if(NULL ==  p2)
    51      {
    52      fprintf(stderr, ":ERROR: couldn't allocate memory for p2\n");
    53      exit(EXIT_FAILURE);
    54      }
    55
    56    fprintf(stderr, "Point2D_get_x(p2): %g Point2D_get_y(p2): %g\n", Point2D_get_x(p2), Point2D_get_y(p2));
    57
    58    p1 = Point2D_clone(p2);
    59
    60    fprintf(stderr, "Point2D_get_x(p1) : %g Point2D_get_y(p1) : %g\n", Point2D_get_x(p1), Point2D_get_y(p1));
    61    fprintf(stderr, "p1=%llx p2=%llx\n", (unsigned long long)(size_t)p1, (unsigned long long)(size_t)p2);
    62
    63    Point2D_deallocate(p2);
    64    Point2D_deallocate(p1);
    65
    66    fprintf(stderr, "END   p2\n\n");
    67
    68    return EXIT_SUCCESS;
    69    }
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> ~/AFSWD/install/gcc-4.4.5/binsh/g++ -O3 -Wall -Wextra -c Point2D_C_interface.cxx -o Point2D_C_interface.o
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> ~/AFSWD/install/gcc-4.4.5/binsh/gcc -O3 -Wall -Wextra main.c Point2D_C_interface.o -o main -lstdc++
sergei@amdam2:~/junk/c++_work/calling_c++_from_c> ./main
sizeof(Point2D)=16

BEGIN on_stack_obj
Point2D(0, 0) called
~Point2D() called
set(9.87, 6.54) called
get_y() called
get_x() called
Point2D_get_x(on_stack_obj): 9.87 Point2D_get_y(on_stack_obj): 6.54
~Point2D() called
END   on_stack_obj

BEGIN p1
Point2D() called
get_y() called
get_x() called
(can be random junk) Point2D_get_x(p1): 0 Point2D_get_y(p1): 0
~Point2D() called
END   p1

BEGIN p2
Point2D(1.23, 4.56) called
get_y() called
get_x() called
Point2D_get_x(p2): 1.23 Point2D_get_y(p2): 4.56
Point2D(const Point2D &other) called
get_y() called
get_x() called
Point2D_get_x(p1) : 1.23 Point2D_get_y(p1) : 4.56
p1=804b020 p2=804b008
~Point2D() called
~Point2D() called
END   p2

sergei@amdam2:~/junk/c++_work/calling_c++_from_c>
 
Old 01-22-2011, 04:26 PM   #12
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
Quote:
Originally Posted by Sergei Steshenko View Post
Code:
INLINE_C void Point2D_cast_to_this_void(void *obj)
  {
  Point2D *t = new Point2D();
  *(static_cast<Point2D * const>(obj)) = *t;
  }
You're leaking memory here, and invoking an assignment operator on something that hasn't been constructed is undefined. You need new (obj) Point2D; (as the entire function,) provided obj points to available memory of the correct size.
Kevin Barry

PS Similarly, in your new code line 24 has undefined behavior, although it probably still works in this situation.

Last edited by ta0kira; 01-22-2011 at 04:28 PM.
 
Old 01-23-2011, 01:05 AM   #13
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Original Poster
Rep: Reputation: 454Reputation: 454Reputation: 454Reputation: 454Reputation: 454
Quote:
Originally Posted by ta0kira View Post
...
PS Similarly, in your new code line 24 has undefined behavior, although it probably still works in this situation.
Do you mean this:

Code:
    18  INLINE_C void Point2D_cast_to_this(ObjType obj)
    19    {
    20    Point2D t(0.0, 0.0); // any constructor call is good here - just to ensure basic initialization
    21                         // in order to be able to call methods later.
    22                         // care should be taken to make sure automatically called destructor of 't'
    23                         // doesn't cause problems.
    24    *obj = t;
    25    }
portion and its line #24 ?

What I mean is to copy everything relevant from 't' onto '*obj'. Actually, I can probably better do it using 'memcpy'.
 
Old 01-23-2011, 09:24 AM   #14
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190Reputation: 1190
Quote:
Originally Posted by Sergei Steshenko View Post
Do you mean this:
Yes. That is what he meant.

Quote:
What I mean is to copy everything relevant from 't' onto '*obj'. Actually, I can probably better do it using 'memcpy'.
Using memcpy would also work, but technically be undefined behavior. It is not any better than misusing the assignment operator.

You should try to understand the "placement new" feature of C++ (ta0kira included an example of the syntax in his last post). That feature is the technically correct way to do both of your Point2D_cast methods that are now technically incorrect.

This "placement new" is a bit confusing because it doesn't allocate any new space. In this default form of placement new, it is really just a call to the constructor on existing space (previously allocated some other way).

C++ does not allow you to directly call a constructor. So in those cases where what you want is a direct call to a constructor, you use placement new.

Placement new can also be used when you want more interesting overrides of the allocation portion of an ordinary new.

Last edited by johnsfine; 01-23-2011 at 09:34 AM.
 
Old 01-23-2011, 10:21 AM   #15
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
Quote:
Originally Posted by johnsfine View Post
Using memcpy would also work...
Unless t holds pointers to dynamic memory that will be deallocated when the function returns.
Kevin Barry
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] Calling Nautilus "file manager" fails but Rhythmbox comes up instead Mark_in_Hollywood Ubuntu 6 12-09-2009 09:09 AM
Facing error while running "g++ 3.4.4" code in "g++ 4.3.3" Suranjit Ubuntu 2 10-08-2009 11:22 PM
usb serial driver failing when calling "usb_submit_urb" - why? DEF. Programming 3 05-28-2009 10:25 AM
ns:"error when calling class OldSim"&tclsh:"invalid command+child process exits abn." shojaru Linux - Newbie 0 03-05-2009 04:23 AM
odd recursion: calling "by hand" vs calling by cronscript... prx Programming 4 02-12-2005 04:59 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 04:22 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration