[PyQt] Simple C++ example has undefined symbol

Gary Fisher gefisher.net at gmail.com
Wed Jan 25 14:08:23 GMT 2012


Hi Jens,

I want to thank you very much for all your help and especially your
patience.

Everything is working perfectly now.

I apologize for the mixed up code...far too much C and not enough C++ in
combination with the old adage "Haste makes waste" !!!

Happy New Year Jens.

         Gary ---




On Sun, Jan 22, 2012 at 1:20 PM, Jens Thoms Toerring <jt at toerring.de> wrote:

> Hi Gary,
>
> On Sun, Jan 22, 2012 at 11:57:44AM -0500, Gary Fisher wrote:
> >         >>> What does "return (static char *)Null" mean?
> >
> > My mistake...I meant "return (const char *)Null" ... not static....and
> yes
> > I do believe NULL is used in C++.  At least the compiler didn't witch at
> me
> > for it.
>
> There's still no reason for the cast. And while you use 'NULL'
> in C quite often it's frowned upon in C++. All you really need
> is
>
>     return 0;
>
> Since the compiler knows that the function returns a 'const char *'
> it will automatically do the conversion. By casting (and then
> even using a C-style cast, which is strongly discouraged in C++)
> you tell the compiler "I know better than you" and you shouldn't
> do that unless there's something the compiler can't figure out
> on his own since a) you keep the compiler from explaining about
> stuff that shouldn't/can't be done (don't see compiler complaints
> as a nuisance but as a valuable help you to write correct pro-
> grams) and b) you make the next person reading your code wonder
> what this is all about.
>
> > When I use "nm -C libword.so" on my little wrapped library I see nothing
> in
> > the output to suggest a "word" class or a "reverse" method.
>
> And that's at the very heart of your problem: the symbols needed
> by the wrapper script don't exist in 'libword.so'. Rhe reason is
> that there's not the slightest bit of necessity for the compiler/
> linker to put them in there: you have a class declared with in-
> lined methods that is never used anywhere. So there's no reason
> to use the class for anything and it simply gets thrown out.
>
> Every program using your library would include a header file for
> the library that defines the interface. And while doing so it
> would see the declaration of the class and would construct it's
> own version of the class when it's needed.
>
> I'm a bit at a loss at understanding how you got your SIP wrapper
> to work without a header file, but that's another topic;-)
>
> The quick fix is to simply splitting the word.cpp file up into
> two parts, the necessary header file with the class declaration
> and a cpp file with the function definitions. I.e. something
> like this:
>
> ------- word.hpp -----------------------------
> #ifndef WORD_HPP_
> #define WORD_HPP_
>
> class Word {
>  public:
>
>    Word( const char * w );
>
>    const char * reverse( );
>
>  private:
>
>    const char * the_word;
> };
>
> #endif
> ------- word.cpp -----------------------------
> #include "word.hpp"
>
> Word::Word( const char * w ) :
>    the_word( w )
> { }
>
> const char *
> Word::reverse( )
> {
>   return 0;
> }
> ----------------------------------------------
>
> BTW: don't forget the 'public:' in the class!
>
> If you now compile this into a shared library it will con-
> tain both the symbols for the constructor and the reverse()
> method. And within the SIP wrapper you include the 'word.hpp'
> header file.
>
>  However, when
> > I run "nm -C word.so" against the library generated by make I get things
> > like:
> >
> > 000021b4 d methods_Word
> >          U Word::Word(char const*)
> >          U Word::reverse() const
> >
> > among other things.
>
> Yes, the 'word.so' library needs those symbols and expects
> them in one of the libraries it was linked with, but they
> aren't anywhere. That will be only noticed when Python tries
> to load the 'word.so' library and thus it fails.
>
> BTW, the code in your program looks a bit broken.
>
> > class Word {
> >   const char *the_word;
> >   //  const char *the_reverse;
> >   char buffer[20], *pb;
> >
> >   int i, len;
> >
> >   Word( const char *w) {
> >       the_word = w;
> >   }
>
> Do you realize that 'the_word' is now set to some memory
> that doesn't belong to this class? It looks as if you
> would like to store a word using this class, but that's
> not what you do - all you keep is a pointer to some
> string somewhere else in memory (and not owned by the
> class instance). And if this memory is used for something
> else then the 'the_word' pointer will point to memory that
> doesn't contain a string anymore. I guess you're coming from
> languages where memory allocation is done for you in the
> background (like in Python), but if you write in C++ (or C)
> you will have to be very careful to do the right thing with
> memory, e.g. just holding a pointer to some memory doesn't
> make it "yours" - you have to ensure that nothing else will
> fiddle with the memory pointed to.
>
> >   const char *reverse () {
> >
> >       return (const char *) NULL;
> >
> >       /************************
> >       len = strlen(the_word);
> >       strcpy (buffer, the_word);
>
> What happens if what 'the_word' points to is longer than 19
> chars? You write past the end of buffer with unforseeable
> effects. In the worst case it even might seem to work at
> firt until some strange and hard to trace erros show up
> much later...
>
> >       pb = (char *)the_word;
>
> That's exactly one of the places were a cast shouldn't be
> used. 'the_word' points to memory you are not allowed to
> change. And to get around this you need the cast. So you're
> now operating directly on the memory where your word is
> stored which could reside in read-only memory.
>
> >       for (i=len-1 ; i>=0 ; i--) {
> >         *pb = buffer[i];
> >         pb++;
> >       }
>
> >       *pb = '\0';
> >
> >       return the_word;
> >   }
> > };
>
> Here's some way you could do it (but note, I didn't carefully
> check everything):
>
> ------- word.hpp -----------------------------
> #ifndef WORD_HPP_
> #define WORD_HPP_
>
> #include <cstring>
>
> class Word {
>  public:
>
>    Word( const char * w );
>
>        ~Word( );
>
>    const char * reverse( );
>
>  private:
>
>    char * the_word;
> };
>
> #endif
> ------- word.cpp -----------------------------
> #include "word.hpp"
>
> Word::Word( const char * w )
> {
>    the_word = new char [ ( w != 0 ? strlen( w ) : 0 ) + 1 ];
>
>    if ( w != 0 )
>        strcpy( the_word, w );
>    else
>        *the_word = '\0';
> }
>
> Word::~Word( )
> {
>    delete [ ] the_word;
> }
>
> const char *
> Word::reverse( )
> {
>    char *ps = the_word,
>         *pe = the_word + strlen( the_word );
>
>    while ( ps < pe )
>    {
>        char tmp = *ps;
>        *ps++ = *pe;
>        *pe-- = tmp;
>    }
>
>    return the_word;
> }
> ----------------------------------------------
>
> But since you write C++ it is probably nuch more reasonable
> (and safer) to use the std::string class instead of creating
> a probably inferior and slower version on your own;-) And all
> this is very C-ish, for example in C++ you would rather return
> a Word instance from the reverse() method instead of a pointer
> to something in the innards of the class...
>
>                            Regards, Jens
> --
>  \   Jens Thoms Toerring  ________      jt at toerring.de
>   \_______________________________      http://toerring.de
>



-- 
--------------------------------------------------------------------------------------------------

 “Nothing in all the world is more dangerous than sincere ignorance and
conscientious stupidity.”

 ~ Martin Luther King, Jr
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20120125/7b8be980/attachment.html>


More information about the PyQt mailing list