[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    Re: [PyQt] Simple C++ example has undefined symbol
From:       Gary Fisher <gefisher.net () gmail ! com>
Date:       2012-01-25 14:08:23
Message-ID: CALhunyZRPLRZGrBQHMK3=TXX+EnX=-bOR1UaM0SRtRhP5YKW_Q () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


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@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@toerring.de
>   \_______________________________      http://toerring.de
>



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

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

 ~ Martin Luther King, Jr

[Attachment #5 (text/html)]

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


<p style="margin-top:0.19in;margin-bottom:0.19in;background:#ffffff">
<font color="#333333">“<font face="Arial, sans-serif"><font>Nothing
in all the world is more dangerous than sincere ignorance </font></font></font><span \
style="font-family:Arial,sans-serif;color:rgb(51,51,51)">and conscientious \
stupidity.”</span></p> <p \
style="margin-top:0.19in;margin-bottom:0.19in;background:#ffffff"> <font face="Arial, \
sans-serif"><font>	~ Martin Luther King, \
Jr</font></font></p></div><div><p></p></div><font size="3" face="Times New Roman">



</font><br>
</div>



_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic