Smart char * class...

Discussion in 'C++' started by Christopher Benson-Manica, Aug 20, 2004.

  1. I need a smart char * class, that acts like a char * in all cases, but
    lets you do some std::string-type stuff with it. (Please don't say to
    use std::string - it's not an option...). This is my attempt at it,
    but it seems to be lacking... I'm aware that strdup() is nonstandard
    (and a bad idea for C++ code) - please just bear with me:

    /* Assume relevant headers are included */

    class char_ptr
    {
    char *value;

    public:
    char_ptr() : value(strdup("")) {}
    ~char_ptr() {free(value);}
    char_ptr( const char *s ) : value(strdup(s?s:"")) {}

    void operator= ( const char * s ) {
    free( value );
    value=strdup( s?s:"" );
    };

    Aside from ignoring the return value of strdup(), is there anything
    that invokes undefined behavior in this definition? If there is not,
    should the following code work or not?

    char_ptr s( "Hello, world!\n" );
    printf( "%s", s );

    I'm aware of the very kludgy nature of this stuff; is there a (better)
    way to accomplish this task? Can it be done at all?

    --
    Christopher Benson-Manica | I *should* know what I'm talking about - if I
    ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
    Christopher Benson-Manica, Aug 20, 2004
    #1
    1. Advertising

  2. Christopher Benson-Manica wrote:
    > I need a smart char * class, that acts like a char * in all cases, but
    > lets you do some std::string-type stuff with it. (Please don't say to
    > use std::string - it's not an option...).


    Could you maybe explain why it's not an option?

    > This is my attempt at it,
    > but it seems to be lacking... I'm aware that strdup() is nonstandard
    > (and a bad idea for C++ code) - please just bear with me:
    >
    > /* Assume relevant headers are included */
    >
    > class char_ptr
    > {
    > char *value;
    >
    > public:
    > char_ptr() : value(strdup("")) {}
    > ~char_ptr() {free(value);}
    > char_ptr( const char *s ) : value(strdup(s?s:"")) {}
    >
    > void operator= ( const char * s ) {
    > free( value );
    > value=strdup( s?s:"" );
    > };
    >
    > Aside from ignoring the return value of strdup(), is there anything
    > that invokes undefined behavior in this definition? If there is not,
    > should the following code work or not?
    >
    > char_ptr s( "Hello, world!\n" );
    > printf( "%s", s );


    No. Passing an object to a function with variable number of arguments
    is undefined. So, printf is not going to work unless you define one
    more member function of 'char_ptr', 'operator const char*':

    ...
    operator const char* () const { return value; }

    .. Then you will need to do

    printf("%s", (const char*)s);

    Also, your class is defective: it is missing a copy constructor, which
    you have to have according to the "Rule of Three".

    > I'm aware of the very kludgy nature of this stuff; is there a (better)
    > way to accomplish this task? Can it be done at all?


    Keep adding functionality to your class until you get it right. Then
    see which functionality you don't need, and get rid of it.

    Victor
    Victor Bazarov, Aug 20, 2004
    #2
    1. Advertising

  3. Victor Bazarov <> spoke thus:

    > Could you maybe explain why it's not an option?


    My boss doesn't like them, and all the code expects char *'s, and none
    of it catches std::exception, so...

    > No. Passing an object to a function with variable number of arguments
    > is undefined. So, printf is not going to work unless you define one
    > more member function of 'char_ptr', 'operator const char*':


    Ah, that's what I wanted! :)

    > Also, your class is defective: it is missing a copy constructor, which
    > you have to have according to the "Rule of Three".


    That was *also* what I wanted, excellent!

    > Keep adding functionality to your class until you get it right. Then
    > see which functionality you don't need, and get rid of it.


    Will do, thanks!

    --
    Christopher Benson-Manica | I *should* know what I'm talking about - if I
    ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
    Christopher Benson-Manica, Aug 20, 2004
    #3
  4. Christopher Benson-Manica

    Kai-Uwe Bux Guest

    Christopher Benson-Manica wrote:

    > I need a smart char * class, that acts like a char * in all cases, but
    > lets you do some std::string-type stuff with it. (Please don't say to
    > use std::string - it's not an option...).


    Why? I would really like to know.

    > This is my attempt at it,
    > but it seems to be lacking... I'm aware that strdup() is nonstandard
    > (and a bad idea for C++ code) - please just bear with me:
    >
    > /* Assume relevant headers are included */
    >
    > class char_ptr
    > {
    > char *value;
    >
    > public:
    > char_ptr() : value(strdup("")) {}
    > ~char_ptr() {free(value);}
    > char_ptr( const char *s ) : value(strdup(s?s:"")) {}
    >
    > void operator= ( const char * s ) {
    > free( value );
    > value=strdup( s?s:"" );
    > };
    >


    I do not see undefined behaviour, but I do see that the assignment operator
    is prone to self-destruct your string in

    char_ptr x;
    ...
    x = x; // <- bang. (This can occur in less abvious forms.)


    > Aside from ignoring the return value of strdup(), is there anything
    > that invokes undefined behavior in this definition? If there is not,
    > should the following code work or not?
    >
    > char_ptr s( "Hello, world!\n" );
    > printf( "%s", s );
    >
    > I'm aware of the very kludgy nature of this stuff; is there a (better)
    > way to accomplish this task? Can it be done at all?
    >


    Now, if this works then the reason would be that the compiler does not do
    any type checking here. I ran a test:

    #include <string.h>
    #include <stdlib.h>

    class char_ptr
    {
    char *value;

    public:
    char_ptr() : value(strdup("")) {}
    ~char_ptr() {free(value);}
    char_ptr( const char *s ) : value(strdup(s?s:"")) {}

    void operator= ( const char * s ) {
    free( value );
    value=strdup( s?s:"" );
    }
    };

    int main ( void ) {
    char_ptr s( "Hello, world!\n" );
    printf( "%s", s );
    }


    This compiles on g++-3.4.0/Linux but segfaults. You need a cast:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>

    class char_ptr
    {
    char *value;

    public:
    char_ptr() : value(strdup("")) {}
    ~char_ptr() {free(value);}
    char_ptr( const char *s ) : value(strdup(s?s:"")) {}

    void operator= ( const char * s ) {
    free( value );
    value=strdup( s?s:"" );
    }

    operator const char * ( void ) const {
    return value;
    }
    };

    int main ( void ) {
    char_ptr s( "Hello, world!\n" );
    printf( "%s", (const char*)s );
    }

    This seems to work, but is flawed as pointed out above.


    Now, if you need a cast anyway, why not use std::string?


    Best

    Kai-Uwe Bux

    ps.: If you really insist, here is a version that I wrote long ago to
    convince myself that I cannot do better than std::string.


    #include <string>

    class
    minimal_string {
    private:

    char* data;
    std::size_t size;

    inline
    void allocate ( size_t __size ) {
    size = __size;
    data = new char [ __size ];
    }

    inline
    void deallocate ( void ) {
    delete[] data;
    }

    inline
    static
    void memcopy ( const char* source, char* dest, size_t size ) {
    std::char_traits<char>::copy( dest, source, size );
    }

    inline
    void copy_string ( const char* str ) {
    // WARNING: [no deallocation]
    /*
    | This is used in the constructor. Therefore,
    | no deallocation can be done.
    */
    if ( str != NULL ) {
    std::size_t l ( std::char_traits<char>::length( str ) + 1 );
    allocate( l );
    memcopy( str, data, l );
    } else {
    // maybe, we should throw something ?
    allocate( 1 );
    *data = 0;
    }
    }

    public:

    minimal_string ( void ) {
    allocate( 1 );
    *data = 0;
    }

    minimal_string ( const char* str ) {
    copy_string( str );
    }

    minimal_string ( const minimal_string & other ) {
    std::size_t l ( other.length() );
    allocate( l );
    memcopy( other.data, data, l );
    }

    ~minimal_string ( void ) {
    delete [] data ;
    }

    const minimal_string & operator= ( const minimal_string & other ) {
    if ( this != &other ) {
    deallocate();
    std::size_t l ( other.length() );
    allocate( l );
    memcopy( other.data, data, l );
    }
    return( *this);
    }

    void setString ( const char* str ) {
    deallocate();
    copy_string( str );
    }

    std::size_t length ( void ) const {
    return( size );
    //return( 1 + std::char_traits<char>::length( data ) );
    }

    operator const char* ( void ) const {
    return( data );
    }

    }; // minimal_string


    Beware, I wrote this before I knew anything about exception safe coding.
    Therefore, it is very likely that this leaks memory when something is
    thrown.
    Kai-Uwe Bux, Aug 20, 2004
    #4
  5. Christopher Benson-Manica

    Shea Martin Guest

    Christopher Benson-Manica wrote:
    > I need a smart char * class, that acts like a char * in all cases, but
    > lets you do some std::string-type stuff with it. (Please don't say to
    > use std::string - it's not an option...). This is my attempt at it,
    > but it seems to be lacking... I'm aware that strdup() is nonstandard
    > (and a bad idea for C++ code) - please just bear with me:
    >
    > /* Assume relevant headers are included */
    >
    > class char_ptr
    > {
    > char *value;
    >
    > public:
    > char_ptr() : value(strdup("")) {}
    > ~char_ptr() {free(value);}
    > char_ptr( const char *s ) : value(strdup(s?s:"")) {}
    >
    > void operator= ( const char * s ) {
    > free( value );
    > value=strdup( s?s:"" );
    > };
    >
    > Aside from ignoring the return value of strdup(), is there anything
    > that invokes undefined behavior in this definition? If there is not,
    > should the following code work or not?
    >
    > char_ptr s( "Hello, world!\n" );
    > printf( "%s", s );
    >
    > I'm aware of the very kludgy nature of this stuff; is there a (better)
    > way to accomplish this task? Can it be done at all?
    >


    I am also in a situation where std::string is not an option. I have created a
    AsciiString class which I have been using in production code for a year now. I
    used Doxygen to make pretty HTML docs. There are some pretty nifty features in
    it. Almost as good as Qt's QString. If you are interested, I can fire off the
    code and docs to you.

    ~Shea M.
    Shea Martin, Aug 20, 2004
    #5
  6. Christopher Benson-Manica

    David Hilsee Guest

    "Victor Bazarov" <> wrote in message
    news:pDsVc.151$09.us.to.verio.net...
    <snip>
    > ...
    > operator const char* () const { return value; }
    >
    > . Then you will need to do
    >
    > printf("%s", (const char*)s);


    To the OP: Another, IMHO slightly safer, option is to write a member
    function like std::string::c_str().

    --
    David Hilseer
    David Hilsee, Aug 21, 2004
    #6
  7. Christopher Benson-Manica

    Ron Natalie Guest

    "Christopher Benson-Manica" <> wrote in message news:cg5l95
    > My boss doesn't like them,


    Emotions shouldn't play into it.

    > and all the code expects char *'s


    The c_str() function provides this

    >, and none of it catches std::exception, so...


    and this makes a difference how? The only thing in std::string that's going
    to throw is the allocation and if you're going to dynamically allocate the
    char's, they're just as likely to throw.

    >
    > > No. Passing an object to a function with variable number of arguments
    > > is undefined. So, printf is not going to work unless you define one
    > > more member function of 'char_ptr', 'operator const char*':

    >
    > Ah, that's what I wanted! :)


    Further, you're going to have to force the conversion in things like printf
    to convert to that.
    Ron Natalie, Aug 21, 2004
    #7
  8. Ron Natalie <> spoke thus:

    > Emotions shouldn't play into it.


    Well, no; I suppose the better statement would have been "he doesn't
    trust them", and given the horrific excuse for an implementation we're
    using, his mistrust isn't completely misplaced...

    > The c_str() function provides this


    Right, but no one (besides me) is willing to call it explicitly.

    > and this makes a difference how? The only thing in std::string that's going
    > to throw is the allocation and if you're going to dynamically allocate the
    > char's, they're just as likely to throw.


    Said implementation has been known to throw for simple operations such
    as assigning the empty string to a std::string.

    > Further, you're going to have to force the conversion in things like printf
    > to convert to that.


    To be standards-compliant, at least; the bulk of the code is so
    non-standard (partly to comply with the whims of said implementation)
    that switching to a reasonable implementation would be prohibitively
    time-consuming and expensive anyway.

    --
    Christopher Benson-Manica | I *should* know what I'm talking about - if I
    ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
    Christopher Benson-Manica, Aug 23, 2004
    #8
  9. Christopher Benson-Manica

    Ron Natalie Guest

    "Christopher Benson-Manica" <> wrote in message news:cgcpuv$rc7
    >
    > Right, but no one (besides me) is willing to call it explicitly.


    In your example, even with a char* conversion operator, you're going
    to have to do SOMETHING explicitly. The implicit conversions just
    make it more likely to screw up
    >
    > > and this makes a difference how? The only thing in std::string that's going
    > > to throw is the allocation and if you're going to dynamically allocate the
    > > char's, they're just as likely to throw.

    >
    > Said implementation has been known to throw for simple operations such
    > as assigning the empty string to a std::string.


    I doubt that highly. What most likely happens is you invoke undefined behavior
    for passing a null char* pointer to the one of the functions in std::string that take
    a char*. Doesn't matter if you are catching std::exception or not, the behavior
    isn't defined at all.
    >
    > > Further, you're going to have to force the conversion in things like printf
    > > to convert to that.

    >
    > To be standards-compliant, at least; the bulk of the code is so
    > non-standard (partly to comply with the whims of said implementation)
    > that switching to a reasonable implementation would be prohibitively
    > time-consuming and expensive anyway.
    >

    Unless you're convinced that a particular function is known to be bad, all you
    are doing is generating a whole mess of new problems by defining your own half-assed
    attempts to reproduce what has already been written.
    Ron Natalie, Aug 23, 2004
    #9
  10. Ron Natalie <> spoke thus:

    > In your example, even with a char* conversion operator, you're going
    > to have to do SOMETHING explicitly. The implicit conversions just
    > make it more likely to screw up


    I suppose so...

    > I doubt that highly. What most likely happens is you invoke undefined behavior
    > for passing a null char* pointer to the one of the functions in std::string that take
    > a char*. Doesn't matter if you are catching std::exception or not, the behavior
    > isn't defined at all.


    This is pre-modern STL here, so you might not want to doubt it quite
    so highly. I'm quite positive of this, as much of this
    implementation's stream library simply does not work like my
    references say it should (as I posted here several months ago).

    > Unless you're convinced that a particular function is known to be bad, all you
    > are doing is generating a whole mess of new problems by defining your own half-assed
    > attempts to reproduce what has already been written.


    Ron, believe me when I say that you're preaching to the choir here;
    you need to talk to my boss :)

    --
    Christopher Benson-Manica | I *should* know what I'm talking about - if I
    ataru(at)cyberspace.org | don't, I need to know. Flames welcome.
    Christopher Benson-Manica, Aug 23, 2004
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. E11
    Replies:
    1
    Views:
    4,750
    Thomas Weidenfeller
    Oct 12, 2005
  2. lovecreatesbeauty
    Replies:
    1
    Views:
    1,043
    Ian Collins
    May 9, 2006
  3. MotoK
    Replies:
    59
    Views:
    1,808
    Keith Thompson
    Sep 15, 2006
  4. Protoman
    Replies:
    28
    Views:
    733
    mlimber
    Aug 14, 2006
  5. smitty1e
    Replies:
    1
    Views:
    252
    Steven D'Aprano
    Jun 17, 2012
Loading...

Share This Page