strings and NULL argument passing

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • sanjay

    strings and NULL argument passing

    Hi,

    I have a doubt about passing values to a function accepting string.

    =============== =============== ========
    #include <iostream>
    using namespace std;

    int main()
    {
    void print(const string&);
    print("hi");
    print(NULL);
    return 0;
    }
    void print(const string& s)
    {
    cout<<"s is "<<s<<endl;
    }
    =============== =============== ========

    The above program compiles successfully but fails at run time because
    NULL is passed as argument in the second call to print.

    Why doesn't the compiler give an error on passing of NULL value?

    How could we check for such arguments in our program when we are using
    strings?

    Regards
    Sanjay Raghani
  • Fred Zwarts

    #2
    Re: strings and NULL argument passing

    "sanjay" <sanjay.raghani @gmail.comwrote in message news:e662e2dd-af14-4245-b26e-0400ad134306@h2 3g2000prf.googl egroups.com...
    Hi,

    I have a doubt about passing values to a function accepting string.

    =============== =============== ========
    #include <iostream>
    using namespace std;

    int main()
    {
    void print(const string&);
    print("hi");
    print(NULL);
    return 0;
    }
    void print(const string& s)
    {
    cout<<"s is "<<s<<endl;
    }
    =============== =============== ========

    The above program compiles successfully but fails at run time because
    NULL is passed as argument in the second call to print.
    I don't think so. Did you check it?
    You can check it by printing the address of s in the function print.
    I think that the error occurs before print is called.
    The compiler tries to create a temporary string and passes the address of that string to print.
    print ("hi"); is compiled as print (string ("hi"));.
    Depending on you definition of NULL,
    print (NULL); may be compiled as print (string (NULL));.
    This may fail during the creation of the temporary, not in the function print.

    Why doesn't the compiler give an error on passing of NULL value?

    How could we check for such arguments in our program when we are using
    strings?
    You can't. The error occurs before your function starts.
    You should check the argument earlier.

    Comment

    • Rolf Magnus

      #3
      Re: strings and NULL argument passing

      sanjay wrote:
      =============== =============== ========
      #include <iostream>
      using namespace std;
      >
      int main()
      {
      void print(const string&);
      print("hi");
      print(NULL);
      return 0;
      }
      void print(const string& s)
      {
      cout<<"s is "<<s<<endl;
      }
      =============== =============== ========
      >
      The above program compiles successfully but fails at run time because
      NULL is passed as argument in the second call to print.
      >
      Why doesn't the compiler give an error on passing of NULL value?
      Well, semantically, NULL can be converted to std::string, just as "hi" can.
      How could the compiler know that NULL isn't a valid value?
      How could we check for such arguments in our program when we are using
      strings?
      I don't see a way to do that, except for providing an overload of the
      function for const char*. The problem is that the error already manifests
      before print is even entered, in the constructor of std::string.
      BTW: The implementation I use throws an exception of type std::logic_erro r
      in this case, but I don't think that this is required by the standard.

      Comment

      • sanjay

        #4
        Re: strings and NULL argument passing

        On Nov 13, 3:19 pm, Rolf Magnus <ramag...@t-online.dewrote:
        sanjay wrote:
        =============== =============== ========
        #include <iostream>
        using namespace std;
        >
        int main()
        {
        void print(const string&);
        print("hi");
        print(NULL);
        return 0;
        }
        void print(const string& s)
        {
        cout<<"s is "<<s<<endl;
        }
        =============== =============== ========
        >
        The above program compiles successfully but fails at run time because
        NULL is passed as argument in the second call to print.
        >
        Why doesn't the compiler give an error on passing of NULL value?
        >
        Well, semantically, NULL can be converted to std::string, just as "hi" can.
        How could the compiler know that NULL isn't a valid value?
        >
        How could we check for such arguments in our program when we are using
        strings?
        >
        I don't see a way to do that, except for providing an overload of the
        function for const char*. The problem is that the error already manifests
        before print is even entered, in the constructor of std::string.
        BTW: The implementation I use throws an exception of type std::logic_erro r
        in this case, but I don't think that this is required by the standard.
        Hi Rolf,

        Thanks for the reply..

        Can you elaborate bit more where exactly do you throw the exception
        for handling such situation?

        Regards
        Sanjay Raghani

        Comment

        • Bo Persson

          #5
          Re: strings and NULL argument passing

          sanjay wrote:
          On Nov 13, 3:19 pm, Rolf Magnus <ramag...@t-online.dewrote:
          >sanjay wrote:
          >>============= =============== ==========
          >>#include <iostream>
          >>using namespace std;
          >>
          >>int main()
          >>{
          >> void print(const string&);
          >> print("hi");
          >> print(NULL);
          >> return 0;
          >>}
          >>void print(const string& s)
          >>{
          >> cout<<"s is "<<s<<endl;
          >>}
          >>============= =============== ==========
          >>
          >>The above program compiles successfully but fails at run time
          >>because NULL is passed as argument in the second call to print.
          >>
          >>Why doesn't the compiler give an error on passing of NULL value?
          >>
          >Well, semantically, NULL can be converted to std::string, just as
          >"hi" can. How could the compiler know that NULL isn't a valid
          >value?
          >>
          >>How could we check for such arguments in our program when we are
          >>using strings?
          >>
          >I don't see a way to do that, except for providing an overload of
          >the function for const char*. The problem is that the error
          >already manifests before print is even entered, in the constructor
          >of std::string.
          >BTW: The implementation I use throws an exception of type
          >std::logic_err or in this case, but I don't think that this is
          >required by the standard.
          >
          Hi Rolf,
          >
          Thanks for the reply..
          >
          Can you elaborate bit more where exactly do you throw the exception
          for handling such situation?
          >
          The constructor for std::string might do that.

          For performance reasons, the std::string constructor taking a const
          char* is NOT required to verify that the pointer is not null. If it
          doesn't, bad things will happen when it tries to determine the length
          of the (non-existant) char sequence pointed to.



          Bo Persson


          Comment

          • Jeff Schwab

            #6
            Re: strings and NULL argument passing

            Rolf Magnus wrote:
            sanjay wrote:
            >#include <iostream>
            >using namespace std;
            >>
            >int main()
            >{
            > void print(const string&);
            > print("hi");
            > print(NULL);
            > return 0;
            >}
            >void print(const string& s)
            >{
            > cout<<"s is "<<s<<endl;
            >}
            >The above program compiles successfully but fails at run time because
            >NULL is passed as argument in the second call to print.
            >>
            >Why doesn't the compiler give an error on passing of NULL value?
            I don't see a way to do that, except for providing an overload of the
            function for const char*. The problem is that the error already manifests
            before print is even entered, in the constructor of std::string.
            Overloading for char const* is a fine option. Using a string type that
            performs the run-time check is also OK. The problem with either of
            those approaches is that it imposes the run-time check, even for C-style
            string literals (e.g. "hi") whose type cannot be null, but which decay
            to the pointer type.

            A function template can be defined to avoid the overhead of the check
            for character array literals. Another, "catch-all" function template
            can generate a compile-time error for any other argument type that could
            otherwise be inadvertently converted to a string.

            #include <iostream>
            #include <string>

            /* Print a standard string. */
            void print(std::stri ng const& s) {
            std::cout << "s is " << s << '\n';
            }

            /* Print a C-style string literal. */
            template<std::s ize_t Size>
            void print(char const (&c_str)[Size]) {
            print(std::stri ng( c_str ));
            }

            /* Generate a compile time error for unacceptable types. */
            template<typena me String>
            void print(String const& s) {
            s.is_not_of_an_ acceptable_stri ng_type();
            }

            int main() {
            print("hi"); // OK
            // print(NULL); // Compile-time error.
            return 0;
            }

            Comment

            • Juha Nieminen

              #7
              Re: strings and NULL argument passing

              sanjay wrote:
              Why doesn't the compiler give an error on passing of NULL value?
              Short answer: Because std::string has a constructor which takes a
              const char* as parameter, and a null pointer is a perfectly valid
              pointer for it. Technically speaking the compiler cannot know if that
              constructor handles a null pointer correctly or not, so it has no reason
              to issue any error.
              How could we check for such arguments in our program when we are using
              strings?
              Make a version of print() which takes a const char* as parameter and
              performs the check. If a non-null pointer is passed, it simply calls the
              print() taking a std::string as parameter, else it performs whatever
              error termination you want (eg. assert()).

              Comment

              • Jeff Schwab

                #8
                Re: strings and NULL argument passing

                Juha Nieminen wrote:
                sanjay wrote:
                >Why doesn't the compiler give an error on passing of NULL value?
                >
                Short answer: Because std::string has a constructor which takes a
                const char* as parameter, and a null pointer is a perfectly valid
                pointer for it. Technically speaking the compiler cannot know if that
                constructor handles a null pointer correctly or not, so it has no reason
                to issue any error.
                >
                >How could we check for such arguments in our program when we are using
                >strings?
                >
                Make a version of print() which takes a const char* as parameter and
                performs the check. If a non-null pointer is passed, it simply calls the
                print() taking a std::string as parameter, else it performs whatever
                error termination you want (eg. assert()).
                "assert" means "I know this is true." It's enforced documentation, not
                a general-purpose way to terminate a program.

                Anyway, there's nothing here that implies termination. An exception can
                be thrown, or the null pointer can just be accepted as an empty string,
                or a C-style error code can be returned. In my experience, the
                exception is usually the right way to go.

                Comment

                • James Kanze

                  #9
                  Re: strings and NULL argument passing

                  On Nov 13, 11:19 am, Rolf Magnus <ramag...@t-online.dewrote:
                  sanjay wrote:
                  =============== =============== ========
                  #include <iostream>
                  using namespace std;
                  int main()
                  {
                      void print(const string&);
                      print("hi");
                      print(NULL);
                      return 0;
                  }
                  void print(const string& s)
                  {
                   cout<<"s is "<<s<<endl;
                  }
                  =============== =============== ========
                  The above program compiles successfully but fails at run
                  time because NULL is passed as argument in the second call
                  to print.
                  Why doesn't the compiler give an error on passing of NULL
                  value?
                  Well, semantically, NULL can be converted to std::string, just
                  as "hi" can. How could the compiler know that NULL isn't a
                  valid value?
                  Maybe we understand "semantical ly" differently, but I would say
                  that his problem is precisely that NULL can't be semantically
                  converted to an std::string. His code only compiles because it
                  is syntactically correct; he provides a value which can be
                  converted to type char const*, and that's all the compiler
                  checks. Semantically, of course, NULL isn't a string, in any
                  sense of the word, and it doesn't make sense to try to convert
                  it to a string. (In C, one might use it to indicate the absense
                  of a string; std::string doesn't support that concept, however.)
                  How could we check for such arguments in our program when we
                  are using strings?
                  I don't see a way to do that, except for providing an overload
                  of the function for const char*.
                  An implementation of std::string could easily cause it to
                  trigger a compiler error; just provide a private constructor
                  which takes some other type of pointer (which would make
                  std::string(NUL L) ambiguous).
                  The problem is that the error already manifests before print
                  is even entered, in the constructor of std::string. BTW: The
                  implementation I use throws an exception of type
                  std::logic_erro r in this case, but I don't think that this is
                  required by the standard.
                  It's undefined behavior. With a good implementation of
                  std::sttring, it won't compile. (Regretfully, I don't know of
                  any implementations which are good by that definition:-).

                  --
                  James Kanze (GABI Software) email:james.kan ze@gmail.com
                  Conseils en informatique orientée objet/
                  Beratung in objektorientier ter Datenverarbeitu ng
                  9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                  Comment

                  • James Kanze

                    #10
                    Re: strings and NULL argument passing

                    On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcen ter.comwrote:
                    Rolf Magnus wrote:
                    sanjay wrote:
                    #include <iostream>
                    using namespace std;
                    [...]
                    Overloading for char const* is a fine option.  Using a string
                    type that performs the run-time check is also OK.  The problem
                    with either of those approaches is that it imposes the
                    run-time check, even for C-style string literals (e.g. "hi")
                    whose type cannot be null, but which decay to the pointer
                    type.
                    Compared to the rest of what the constructor has to do, I rather
                    suspect that the run-time cost of checking isn't measurable.
                    A function template can be defined to avoid the overhead of
                    the check for character array literals.  Another, "catch-all"
                    function template can generate a compile-time error for any
                    other argument type that could otherwise be inadvertently
                    converted to a string.
                    #include <iostream>
                    #include <string>
                    /* Print a standard string. */
                    void print(std::stri ng const& s) {
                         std::cout << "s is " << s << '\n';
                    }
                    /* Print a C-style string literal. */
                    template<std::s ize_t Size>
                    void print(char const (&c_str)[Size]) {
                         print(std::stri ng( c_str ));
                    Or better yet:
                    print( std::string( c_str, Size - 1 ) ) ;

                    No need to count the characters if you already know how many
                    there are.

                    Of course, this fails if the string literal was "a\0b", or
                    something of the sort. It also doesn't work (but nor does your
                    suggestion) when interfacing with C (where all you've got is a
                    char const*).
                    }
                    /* Generate a compile time error for unacceptable types. */
                    template<typena me String>
                    void print(String const& s) {
                         s.is_not_of_an_ acceptable_stri ng_type();
                    }
                    As pointed out earlier, this trick (with some adaption) could be
                    used directly in std::string.

                    It's not a panacea, however. You really do have to support
                    constructing strings from char const*, which can be a null
                    pointer, even if it isn't a literal. (Of course, it can also be
                    an invalid pointer, and there's no way you can check for that.)

                    --
                    James Kanze (GABI Software) email:james.kan ze@gmail.com
                    Conseils en informatique orientée objet/
                    Beratung in objektorientier ter Datenverarbeitu ng
                    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                    Comment

                    • James Kanze

                      #11
                      Re: strings and NULL argument passing

                      On Nov 13, 9:00 pm, Jeff Schwab <j...@schwabcen ter.comwrote:
                      [...]
                      Make a version of print() which takes a const char* as
                      parameter and performs the check. If a non-null pointer is
                      passed, it simply calls the print() taking a std::string as
                      parameter, else it performs whatever error termination you
                      want (eg. assert()).
                      "assert" means "I know this is true."  It's enforced
                      documentation, not a general-purpose way to terminate a
                      program.
                      Anyway, there's nothing here that implies termination.  An
                      exception can be thrown, or the null pointer can just be
                      accepted as an empty string, or a C-style error code can be
                      returned.  In my experience, the exception is usually the
                      right way to go.
                      A null pointer is not a string (in the general sense), nor is it
                      something which can be converted into a string. If his
                      interface requires a string, then passing it a null pointer
                      should cause an assertion failure. If his interface supports
                      the idea of a nullable string (e.g. something you might get when
                      reading a VARCHAR field from a database), then it has to support
                      something more than std::string.

                      --
                      James Kanze (GABI Software) email:james.kan ze@gmail.com
                      Conseils en informatique orientée objet/
                      Beratung in objektorientier ter Datenverarbeitu ng
                      9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

                      Comment

                      • Jeff Schwab

                        #12
                        Re: strings and NULL argument passing

                        James Kanze wrote:
                        On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcen ter.comwrote:
                        >Rolf Magnus wrote:
                        >>sanjay wrote:
                        [snipped code that (oops) initialized a std::string from NULL]
                        >Overloading for char const* is a fine option. Using a string
                        >type that performs the run-time check is also OK. The problem
                        >with either of those approaches is that it imposes the
                        >run-time check, even for C-style string literals (e.g. "hi")
                        >whose type cannot be null, but which decay to the pointer
                        >type.
                        >
                        Compared to the rest of what the constructor has to do, I rather
                        suspect that the run-time cost of checking isn't measurable.
                        Which constructor? std::string? If the OP's std::string isn't
                        detecting null initializers, the decision apparently was made by the
                        implementor that the check was expensive enough to avoid. It may also
                        be possible (and is, in the OP's case) to avoid the std::string
                        altogether, by working directly with the c-style string.
                        >/* Print a C-style string literal. */
                        >template<std:: size_t Size>
                        >void print(char const (&c_str)[Size]) {
                        > print(std::stri ng( c_str ));
                        >
                        Or better yet:
                        print( std::string( c_str, Size - 1 ) ) ;
                        >
                        No need to count the characters if you already know how many
                        there are.
                        Nice catch.
                        Of course, this fails if the string literal was "a\0b", or
                        something of the sort. It also doesn't work (but nor does your
                        suggestion) when interfacing with C (where all you've got is a
                        char const*).
                        The template cannot even be declared in a C header, so it is clearly not
                        meant to be a C-language interface function. I'm not sure why you bring
                        that up; I don't see it as relevant here. If the function is to be
                        callable from C, it also cannot be overloaded for char const* and
                        std::string, since it must be declared extern "C". This is possible
                        only in C++.
                        >/* Generate a compile time error for unacceptable types. */
                        >template<typen ame String>
                        >void print(String const& s) {
                        > s.is_not_of_an_ acceptable_stri ng_type();
                        >}
                        >
                        As pointed out earlier, this trick (with some adaption) could be
                        used directly in std::string.
                        >
                        It's not a panacea, however.
                        It is only (intended to be) an optimization of the run-time code.
                        You really do have to support
                        constructing strings from char const*, which can be a null
                        pointer, even if it isn't a literal.
                        I don't have to support any such thing. Of course, if the client writes
                        something like print(std::stri ng(0)), there's not much I can do. There
                        is a definite trade-off between convenience and safety.
                        (Of course, it can also be
                        an invalid pointer, and there's no way you can check for that.)
                        I don't know of a fool-proof way, but you can sometimes detect nonsense
                        if you control the memory allocation. You can check that the pointer
                        value is within (or outside) some range. You can also catch a bunch of
                        bugs by stomping on all deallocated memory with a magic byte pattern (I
                        like 0xDeadBeef) and checking for that byte pattern in the addressed memory.

                        Comment

                        • Jeff Schwab

                          #13
                          Re: strings and NULL argument passing

                          James Kanze wrote:
                          On Nov 13, 9:00 pm, Jeff Schwab <j...@schwabcen ter.comwrote:
                          [...]
                          >>Make a version of print() which takes a const char* as
                          >>parameter and performs the check. If a non-null pointer is
                          >>passed, it simply calls the print() taking a std::string as
                          >>parameter, else it performs whatever error termination you
                          >>want (eg. assert()).
                          >
                          >"assert" means "I know this is true." It's enforced
                          >documentatio n, not a general-purpose way to terminate a
                          >program.
                          A null pointer is not a string (in the general sense), nor is it
                          something which can be converted into a string.
                          Agreed. Passing null to a function expecting a std::string is an error.
                          If his
                          interface requires a string, then passing it a null pointer
                          should cause an assertion failure.
                          That does not follow. I consider it an abuse of assertions to use them
                          as detectors of contract violation. Assertions are often appropriate
                          for post-conditions, but rarely for pre-conditions.

                          Exceptions should, in my opinion, not be part of the interface
                          definition of functions; exceptions are best reserved, for
                          error-reporting, and that specifically includes run-time contract
                          violations. In the case at hand, std::invalid_ar gument (or a
                          derivative) seems obviously to be the best choice.
                          If his interface supports
                          the idea of a nullable string (e.g. something you might get when
                          reading a VARCHAR field from a database), then it has to support
                          something more than std::string.
                          Yes.

                          Comment

                          • Rolf Magnus

                            #14
                            Re: strings and NULL argument passing

                            Jeff Schwab wrote:
                            James Kanze wrote:
                            >On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcen ter.comwrote:
                            >>Rolf Magnus wrote:
                            >>>sanjay wrote:
                            >
                            [snipped code that (oops) initialized a std::string from NULL]
                            >
                            >>Overloading for char const* is a fine option. Using a string
                            >>type that performs the run-time check is also OK. The problem
                            >>with either of those approaches is that it imposes the
                            >>run-time check, even for C-style string literals (e.g. "hi")
                            >>whose type cannot be null, but which decay to the pointer
                            >>type.
                            >>
                            >Compared to the rest of what the constructor has to do, I rather
                            >suspect that the run-time cost of checking isn't measurable.
                            >
                            Which constructor? std::string?
                            Yes. The memory allocation alone would outweigh that simple check by far.
                            If the OP's std::string isn't detecting null initializers, the decision
                            apparently was made by the implementor that the check was expensive
                            enough to avoid.
                            The problem is that the C++ standard doesn't require it, so I guess some
                            library implementor could think that there is no point in doing such a
                            check, since the user can't rely on it anyway.

                            Comment

                            • Rolf Magnus

                              #15
                              Re: strings and NULL argument passing

                              James Kanze wrote:

                              >Well, semantically, NULL can be converted to std::string, just
                              >as "hi" can. How could the compiler know that NULL isn't a
                              >valid value?
                              >
                              Maybe we understand "semantical ly" differently, but I would say
                              that his problem is precisely that NULL can't be semantically
                              converted to an std::string. His code only compiles because it
                              is syntactically correct;
                              Yes, I guess you're right.
                              How could we check for such arguments in our program when we
                              are using strings?
                              >
                              >I don't see a way to do that, except for providing an overload
                              >of the function for const char*.
                              >
                              An implementation of std::string could easily cause it to
                              trigger a compiler error; just provide a private constructor
                              which takes some other type of pointer (which would make
                              std::string(NUL L) ambiguous).
                              What I meant was that I see no way without altering standard headers.
                              >The problem is that the error already manifests before print
                              >is even entered, in the constructor of std::string. BTW: The
                              >implementati on I use throws an exception of type
                              >std::logic_err or in this case, but I don't think that this is
                              >required by the standard.
                              >
                              It's undefined behavior. With a good implementation of
                              std::sttring, it won't compile.
                              Would it actually be allowed by the standard to have an additional
                              constructor in std::string?


                              Comment

                              Working...