Dilemma of const-ifying a function parameter

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • hzmonte@hotmail.com

    Dilemma of const-ifying a function parameter

    Correct me if I am wrong, declaring formal parameters of functions as
    const, if they should not be/is not changed, has 2 benefits;
    1. It tells the program that calls this function that the parameter
    will not be changed - so don't worry.
    2. It tells the implementor and the maintainer of this function that
    the parameter should not be changed inside the function. And it is for
    this reason that some people advocate that it is a good idea to
    const-ify even a non-pointer parameter:
    void my_func(const int i) { ... }
    because if the original programmer or a subsequent maintainer
    accidentally change the value of the parameter, the compiler can catch
    it.
    A third benefit may be that the compiler can optimize the program if it
    knows for sure that the parameter is not changed inside the function. I
    am not sure about this though.
    Now, I would like to benefit from the const-ification of the function
    parameter. Let's say I have this code:

    typedef struct s_fifoItem t_fifoItem;
    struct s_fifoItem {
    t_fifoItem *next;
    t_fifoItem *prev;
    void *elem;
    };
    typedef t_fifoItem t_fifoQueue;

    void enqueue(t_fifoQ ueue *q, void *elem)
    {
    t_fifoItem *i, *last;

    i = malloc(sizeof(t _fifoItem));
    i->elem = elem;
    last = q->prev;
    last->next = i;
    i->next = q;
    q->prev = i;
    }

    elem appears to be a good candidate for const-ification. Intutitively
    the program calling enqueue() normally does not expect that the item to
    be enqueued is to be changed. And the implementor of enqueue() would
    not want to change elem either; and actually elem is not changed in the
    above implementation. But if I const-ify elem, gcc would complain (in a
    warning) that the assignment
    i->elem = elem;
    would discard the const qualifier because the type of "i->elem" is
    "void *", not "const void *". One solution is to cast away the
    const-ness of elem in that assignment:
    i->elem = (void *)elem;
    Another solution is to change the declaration of elem in struct
    s_fifoItem to be:
    const void *elem;
    The latter solution is not practical because there are other places
    that will change *elem.
    However, the former solution of casting away the const-ness is not very
    appealing. It is kind of cheating.
    So, how do people resolve this? And to a deeper level, gcc seems to be
    caught bewteen 2 conflicting goals: ensuring type correctness (a double
    cannot be assigned to an int, a const void * cannot be assigned to a
    void *, etc) and ensuring the unmodifiability of a variable. From the
    point of view of language design, what are the issues involved and how
    do other programming languages deal with it?

    P.S. I know nothing about C++. But in its queue STL, there is a
    function
    void push(const T &val)
    The const here means that the element is a const parameter. This
    confirms my belief that it is good programming practice to const-ify
    the element to be enqueued. But I wonder how this const-ification is
    implemented inside push(). It is possible that the parameter is made
    constant in the prototype from the client's perspective (to reap the
    first benefit I mentioned above) but the parameter is cast (void *)
    inside the function (so the second benefit is not reaped, and the STL
    coders (i.e. ones who code the STL) need to be careful).

  • Michael Mair

    #2
    Re: Dilemma of const-ifying a function parameter

    hzmonte@hotmail .com schrieb:
    Correct me if I am wrong, declaring formal parameters of functions as
    const, if they should not be/is not changed, has 2 benefits;
    1. It tells the program that calls this function that the parameter
    will not be changed - so don't worry.
    2. It tells the implementor and the maintainer of this function that
    the parameter should not be changed inside the function. And it is for
    this reason that some people advocate that it is a good idea to
    const-ify even a non-pointer parameter:
    void my_func(const int i) { ... }
    Here you are going wrong.
    There is a conceptual difference between
    void my_pfunc (const int *pI);
    and
    void my_func (const int i);
    For the former, whatever pI points to must not be modified
    but pI may be modified. The equivalent of the latter is
    void my_pfunc2 (int * const pI);
    which does not allow to modify the _value_ of the parameter.
    As the change of the parameter value has no bearing whatsoever
    on the argument being passed, there is indeed only the local
    "benefit" of not being able to change the parameter.
    This IMO is more a question of style than anything else.
    If you want to do that consequently and have benefit 1 and 2
    on top of it, you arrive at
    const Type
    <some number of "* const" and plain "*" in between>
    ParameterName
    i.e. you will have
    const int * const pI
    for the above and
    const int ** const ppI
    or
    const int * const * const ppI
    to be safe on either the level of the values at the bottom
    and level of the pointer value or on all levels.

    because if the original programmer or a subsequent maintainer
    accidentally change the value of the parameter, the compiler can catch
    it.
    A third benefit may be that the compiler can optimize the program if it
    knows for sure that the parameter is not changed inside the function. I
    am not sure about this though.
    Now, I would like to benefit from the const-ification of the function
    parameter. Let's say I have this code:
    >
    typedef struct s_fifoItem t_fifoItem;
    struct s_fifoItem {
    t_fifoItem *next;
    t_fifoItem *prev;
    void *elem;
    [*]
    };
    typedef t_fifoItem t_fifoQueue;
    >
    void enqueue(t_fifoQ ueue *q, void *elem)
    {
    t_fifoItem *i, *last;
    >
    i = malloc(sizeof(t _fifoItem));
    You forgot to check the return value of malloc().
    Undefined behaviour from here.
    Note that
    i = malloc(sizeof *i);
    is safer in that you have not to adapt the line if the
    type of i changes (e.g. copy&paste+adap tion).
    i->elem = elem;
    Make[*]
    const void *elem
    if you want to never ever modify *elem via i->elem
    and never to be able to modify *(i->elem) when you get
    the address from i->elem instead of another copy of the
    address.
    Then "const"ing *elem via "const void *elem" makes sense.
    This is only the case if you want to store the address for
    comparisons of either address or value pointed to.
    last = q->prev;
    last->next = i;
    i->next = q;
    q->prev = i;
    }
    >
    elem appears to be a good candidate for const-ification. Intutitively
    the program calling enqueue() normally does not expect that the item to
    be enqueued is to be changed. And the implementor of enqueue() would
    not want to change elem either; and actually elem is not changed in the
    above implementation. But if I const-ify elem, gcc would complain (in a
    warning) that the assignment
    i->elem = elem;
    would discard the const qualifier because the type of "i->elem" is
    "void *", not "const void *". One solution is to cast away the
    const-ness of elem in that assignment:
    i->elem = (void *)elem;
    Another solution is to change the declaration of elem in struct
    s_fifoItem to be:
    const void *elem;
    The latter solution is not practical because there are other places
    that will change *elem.
    However, the former solution of casting away the const-ness is not very
    appealing. It is kind of cheating.
    There is a difference between "the pointer parameter will not be
    used in the function to modify what the pointer points to" and
    "the address passed via the pointer parameter and its copies made
    during the function's lifetime are not used to modify what the
    pointer points to". You essentially promise the latter when saying
    "const void *elem".
    Once again: const-ifying elem means "void * const elem" and that
    the parameter will not change its value. If you want to be able
    to say "the value elem points to is not changed but the value
    i->elem points to may be changed" then you have also to pass the
    size of *elem and create a copy of *elem:
    i->elem = malloc(size);
    if (i->elem == NULL) {
    /* Your sensible action here */
    }
    memcpy(i->elem, elem, size);
    So, how do people resolve this? And to a deeper level, gcc seems to be
    caught bewteen 2 conflicting goals: ensuring type correctness (a double
    cannot be assigned to an int, a const void * cannot be assigned to a
    void *, etc) and ensuring the unmodifiability of a variable. From the
    point of view of language design, what are the issues involved and how
    do other programming languages deal with it?
    gcc just does the thing the language is promising.

    <OT>
    P.S. I know nothing about C++. But in its queue STL, there is a
    function
    void push(const T &val)
    The const here means that the element is a const parameter. This
    confirms my belief that it is good programming practice to const-ify
    the element to be enqueued. But I wonder how this const-ification is
    implemented inside push(). It is possible that the parameter is made
    constant in the prototype from the client's perspective (to reap the
    first benefit I mentioned above) but the parameter is cast (void *)
    inside the function (so the second benefit is not reaped, and the STL
    coders (i.e. ones who code the STL) need to be careful).
    Note that for C++ templates, the type of the passed val is known,
    thus a local copy can be made, just as I suggested above.
    If the queue stored a "const T *" or "const T&" copy of val, then
    you have once again a pointer or reference to dVal=(whatever
    dereferencing val gives you) that cannot be used to change dVal.
    If you change whatever is stored in the queue, then you have no
    changes whatsoever to dVal.

    This is just the same as giving &dElem to enqueue() -- the
    prototype of enqueue promises you that dElem will never ever
    be changed by the address given to enqueue() or one of its
    copies made during the run-time of enqueue(). At least if
    enqueue() is honouring the promise of "const void * elem".
    </OT>


    Cheers
    Michael
    --
    E-Mail: Mine is an /at/ gmx /dot/ de address.

    Comment

    • Malcolm

      #3
      Re: Dilemma of const-ifying a function parameter




      <hzmonte@hotmai l.comwrote in message
      >
      elem appears to be a good candidate for const-ification. Intutitively
      the program calling enqueue() normally does not expect that the item to
      be enqueued is to be changed. And the implementor of enqueue() would
      not want to change elem either; and actually elem is not changed in the
      above implementation. But if I const-ify elem, gcc would complain (in a
      warning) that the assignment
      i->elem = elem;
      would discard the const qualifier because the type of "i->elem" is
      "void *", not "const void *". One solution is to cast away the
      const-ness of elem in that assignment:
      i->elem = (void *)elem;
      Another solution is to change the declaration of elem in struct
      s_fifoItem to be:
      const void *elem;
      The latter solution is not practical because there are other places
      that will change *elem.
      >
      What you are describing is const poisoning. Very few variables, by
      definition, are truly constant through the life of the program. However they
      may be constant for a defined period of time.
      const was added to C by a committee. Committees are good at taking certain
      types of decisions, but language design isn't one of them. A good language
      is more like a Renasissance masterpiece than a ladies' knitting circle tea
      rota. It doesn't express the constness of data very well.
      The question is, what to do about it? There is no good answer. If you reject
      const algoether you risk making code uncallable by those folish enough to
      use it, if you use it you get these types of problems.



      --

      freeware games to download.


      Comment

      • Bill Pursell

        #4
        Re: Dilemma of const-ifying a function parameter

        hzmonte@hotmail .com wrote:
        Correct me if I am wrong, declaring formal parameters of functions as
        const, if they should not be/is not changed, has 2 benefits;
        1. It tells the program that calls this function that the parameter
        will not be changed - so don't worry.
        2. It tells the implementor and the maintainer of this function that
        the parameter should not be changed inside the function. And it is for
        this reason that some people advocate that it is a good idea to
        const-ify even a non-pointer parameter:
        void my_func(const int i) { ... }
        because if the original programmer or a subsequent maintainer
        accidentally change the value of the parameter, the compiler can catch
        it.
        FWIW, I don't ever see the point of doing this. If you want
        that type of behavior, it's much more reasonable to do:
        void my_func(int i) { const int i_copy = i; ...}. If the problem
        is something like:
        void my_func(len I)
        {
        for ( ; len 0 ; len--)
        ;
        /* oops, future maintainer thinks len is still valid */
        },

        then making len const just forces you to add a loop
        variable and the new code becomes the above
        suggestion, with the const transposed. Usually, when
        I see foo(const int arg) I take it as a clue that the
        author is a newbie and it raises warning flags.
        A third benefit may be that the compiler can optimize the program if it
        knows for sure that the parameter is not changed inside the function. I
        am not sure about this though.
        The restrict keyword is probably more beneficial for optimization,
        but I believe const has an effect as well. (I have no empirical
        evidence to support this claim.)

        <snip>
        One solution is to cast away the
        const-ness of elem in that assignment:
        i->elem = (void *)elem;
        Another solution is to change the declaration of elem in struct
        s_fifoItem to be:
        const void *elem;
        The latter solution is not practical because there are other places
        that will change *elem.
        I would go with the former, but a slight modification
        of the second solution is to provide an accessor function
        which casts away the const from the structure. eg
        void *ret;
        ret = (void *)(i->elem)
        This has essentially exactly the same problems you
        describe, but it localizes the cast.
        However, the former solution of casting away the const-ness is not very
        appealing. It is kind of cheating.
        So, how do people resolve this?
        I've been wondering about this for some time, and I'm starting
        to lean towards using const less often than I'd like. For example,
        consider an implementation of strchr:
        error_t my_strchr(const char *string, char target, const char
        **location);
        Where instead of returning the index, you put a pointer to the
        first occurence of target in *location. If you don't constify
        location, you'll need to cast its assignment in my_strchr. If you
        do constify it, then the caller must always pass consts: ie to
        replace 'a' with 'b', you'd like to do:

        while (my_strchr( string, 'a', &location)) {
        *location = 'b';
        }

        but instead you'll need to cast away the const of location. Again,
        I think the solution here is to cast the assignment within my_strchr.
        It's aesthically unappealing, but IMO it's the correct thing to do.

        --
        Bill Pursell

        Comment

        • Michal Nazarewicz

          #5
          Re: Dilemma of const-ifying a function parameter

          hzmonte@hotmail .com writes:
          A third benefit may be that the compiler can optimize the program if it
          knows for sure that the parameter is not changed inside the function. I
          am not sure about this though.
          No.

          #include <stdio.h>
          void foo(const int *a, int *b) {
          printf("*a = %d; *b = %d\n", *a, *b);
          *b++;
          printf("*a = %d; *b = %d\n", *a, *b);
          }
          int main(void) {
          int c = 1;
          foo(&c, &c);
          return 0;
          }


          There is no way compiler can tell whether the thing pointed by a const
          int* pointer will change or not. You'll had to use 'restrict'
          keyword for that:

          void foo(const int restrict *a, int restrict *b) { /* ... */

          However, still it's up to programmer not compiler to guarantee that
          a and b point to different things.
          struct s_fifoItem {
          t_fifoItem *next;
          t_fifoItem *prev;
          void *elem;
          };
          [...]
          void enqueue(t_fifoQ ueue *q, void *elem) {
          t_fifoItem *i, *last;
          i = malloc(sizeof(t _fifoItem));
          i->elem = elem;
          [...]
          }
          >
          elem appears to be a good candidate for const-ification.
          You'll have to constify it in the structure also and it may or may not
          break your code depending on what you do with the pointer.
          One solution is to cast away the const-ness of elem in that
          assignment:
          Then, constifing the formal parameter would be pointless.
          Another solution is to change the declaration of elem in struct
          s_fifoItem to be:
          const void *elem;
          And that's what you have to do.
          The latter solution is not practical because there are other places
          that will change *elem.
          So you cannot constify the parameter. The function doesn't change
          what elem points to but it doesn't matter. It saves the pointer
          somewhere for other functions to be able to modify it so indirectly
          the function is modifying it.
          However, the former solution of casting away the const-ness is not very
          appealing. It is kind of cheating.
          It's stupid and pointless. :)
          So, how do people resolve this?
          If any other function will modify what elem points to you cannot
          constify the argument. Compare this to that code:

          void inc(int *bar) { ++*bar; }
          void foo(const int *const_bar) { inc((int *)const_bar); }

          Your saying: OK, foo() doesn't modify *const_bar so we can constify it
          but at the same time inc() modifies it so we need to cast it to
          non-const pointer but in the end doesn't foo() end modifying
          *const_bar?
          P.S. I know nothing about C++. But in its queue STL, there is a
          function
          void push(const T &val)
          The const here means that the element is a const parameter. This
          confirms my belief that it is good programming practice to const-ify
          the element to be enqueued.
          I don't know STL but probably there is also a const T &pop() method
          which returns *const* reference or val is being *copied* and then
          pop() returns non-const reference to the copy. In your case, you want
          a void push(const T *val) and T *pop() functions which is wrong.
          It is possible that the parameter is made constant in the prototype
          from the client's perspective (to reap the first benefit I mentioned
          above) but the parameter is cast (void *)
          No.

          --
          Best regards, _ _
          .o. | Liege of Serenly Enlightened Majesty of o' \,=./ `o
          ..o | Computer Science, Michal "mina86" Nazarewicz (o o)
          ooo +--<mina86*tlen.pl >--<jid:mina86*jab ber.org>--ooO--(_)--Ooo--

          Comment

          • Frederick Gotham

            #6
            Re: Dilemma of const-ifying a function parameter

            hzmonte posted:
            Correct me if I am wrong, declaring formal parameters of functions as
            const, if they should not be/is not changed, has 2 benefits;

            I use the exact same rules for defining function arguments as const, as I
            use for defining any object as const.

            1. It tells the program that calls this function that the parameter
            will not be changed - so don't worry.
            2. It tells the implementor and the maintainer of this function that
            the parameter should not be changed inside the function. And it is for
            this reason that some people advocate that it is a good idea to
            const-ify even a non-pointer parameter:
            void my_func(const int i) { ... }

            Yes, I would define "i" as const if it weren't to be changed.

            void enqueue(t_fifoQ ueue *q, void *elem)
            {
            t_fifoItem *i, *last;
            >
            i = malloc(sizeof(t _fifoItem));
            i->elem = elem;
            last = q->prev;
            last->next = i;
            i->next = q;
            q->prev = i;
            }
            >
            elem appears to be a good candidate for const-ification.

            Indeed:

            void *const elem;

            Intutitively the program calling enqueue() normally does not expect that
            the item to be enqueued is to be changed. And the implementor of
            enqueue() would not want to change elem either; and actually elem is not
            changed in the above implementation. But if I const-ify elem, gcc would
            complain (in a warning) that the assignment
            i->elem = elem;
            would discard the const qualifier because the type of "i->elem" is
            "void *", not "const void *". One solution is to cast away the
            const-ness of elem in that assignment:
            i->elem = (void *)elem;

            No no no! Leave the function argument as a "pointer to const".

            There's a difference between an actual object being const, e.g.:

            int const i;
            int *const p;

            , and a pointer to const:

            int const *p;

            If you change the function parameter to "pointer to NON-const", then
            there's the danger of the following:

            char const buf[5] = {0};
            Func(buf);

            Because the address of the array will eventually be held in a "void*"
            (which is a pointer to NON-const), there's the danger of it being altered
            at some point.

            So, how do people resolve this? And to a deeper level, gcc seems to be
            caught bewteen 2 conflicting goals: ensuring type correctness (a double
            cannot be assigned to an int, a const void * cannot be assigned to a
            void *, etc) and ensuring the unmodifiability of a variable. From the
            point of view of language design, what are the issues involved and how
            do other programming languages deal with it?

            First thing to tackle is this:

            Should "s_fifoItem " contain a "void*", or a "void const*". If the
            former, then this implies that the pointer will be used to alter the data
            at that address. If the latter, this implies that the data won't be
            altered. If you choose to leave it as "void*", then make your function take
            a "void*".

            P.S. I know nothing about C++. But in its queue STL, there is a
            function
            void push(const T &val)
            The const here means that the element is a const parameter.

            No it doesn't -- it means that val is a reference to a const T.

            (A reference can't be const because it isn't an object -- the following
            won't compile: int &const i)

            --

            Frederick Gotham

            Comment

            • hzmonte@hotmail.com

              #7
              Re: Dilemma of const-ifying a function parameter

              Here you are going wrong.
              Where did I go wrong? I did make a mistake by saying constifying elem;
              what I meant was constifying *elem. That is, the data pointed to by
              the pointer is not changed.
              Make[*]
              const void *elem
              if you want to never ever modify *elem via i->elem
              and never to be able to modify *(i->elem) when you get
              the address from i->elem instead of another copy of the
              address.
              Then "const"ing *elem via "const void *elem" makes sense.
              I do want to modify *elem outside this (enqueue) function. That is why
              I said I have a dilemma.
              There is a difference between "the pointer parameter will not be
              used in the function to modify what the pointer points to" and
              "the address passed via the pointer parameter and its copies made
              during the function's lifetime are not used to modify what the
              pointer points to". You essentially promise the latter when saying
              "const void *elem".
              Yes, I want that (latter) promise.
              Once again: const-ifying elem means "void * const elem" and that
              Again, I should have said "const-ifying *elem".
              the parameter will not change its value. If you want to be able
              to say "the value elem points to is not changed but the value
              i->elem points to may be changed" then you have also to pass the
              size of *elem and create a copy of *elem:
              i->elem = malloc(size);
              if (i->elem == NULL) {
              /* Your sensible action here */
              }
              memcpy(i->elem, elem, size);
              It would be more precise to say that what I want is "the value elem
              points to is not changed within the function but the value i->elem
              points to may be changed outside the function".
              Your suggestion resolved the problem but requires additional memory
              that is not necessary had C provided a way to get around this dilemma.
              There is no real need to change *elem in the function and thus no need
              to malloc a copy of *elem - the need arises only because C does not
              satisfy both goals (type compatibility and the desirability to
              represent a parameter as "does not change" in a function) at the same
              time.

              Comment

              • hzmonte@hotmail.com

                #8
                Re: Dilemma of const-ifying a function parameter


                Michal Nazarewicz wrote:
                One solution is to cast away the const-ness of elem in that assignment:
                >
                Then, constifing the formal parameter would be pointless.
                It is not pointless. Constifying the formal parameter tells the
                calling function that "Hey, don't worry, I will not change *elem within
                me." The caller needs not worry about any side effect.
                Another solution is to change the declaration of elem in struct
                s_fifoItem to be:
                const void *elem;
                >
                And that's what you have to do.
                >
                The latter solution is not practical because there are other places
                that will change *elem.
                >
                So you cannot constify the parameter. The function doesn't change
                what elem points to but it doesn't matter. It saves the pointer
                somewhere for other functions to be able to modify it so indirectly
                the function is modifying it.
                It matters because constifying a parameter makes a promise that it is
                not changed inside the function. Conversely speaking, if the parameter
                is not changed inside the function, then it can be const-ified. If the
                parameter is not changed inside the function and it still cannot be
                const-ified, then what is the use of "const"?
                I am inclined to disagreeing to your "indirect modification" argument.
                I think "const" promises that the parameter is not modified inside the
                function. It is a local promise. My understanding is that it is never
                intended to take into consideration whether the parameter would be
                modified outside the function, probably as a result of some action
                inside the function.
                >
                However, the former solution of casting away the const-ness is not very
                appealing. It is kind of cheating.
                If any other function will modify what elem points to you cannot
                constify the argument. Compare this to that code:
                >
                void inc(int *bar) { ++*bar; }
                void foo(const int *const_bar) { inc((int *)const_bar); }
                >
                Your saying: OK, foo() doesn't modify *const_bar so we can constify it
                but at the same time inc() modifies it so we need to cast it to
                non-const pointer but in the end doesn't foo() end modifying
                *const_bar?
                The difference between your example and my enqueue() example is that in
                foo(), you do modify the parameter - the semantics of foo is that the
                parameter is incremented, but in enqueue(), *elem is not modified - no
                one expect by purely putting a data to a queue would modify the data.

                Comment

                • hzmonte@hotmail.com

                  #9
                  Re: Dilemma of const-ifying a function parameter

                  Frederick Gotham wrote:
                  Should "s_fifoItem " contain a "void*", or a "void const*". If the
                  former, then this implies that the pointer will be used to alter the data
                  at that address. If the latter, this implies that the data won't be
                  altered. If you choose to leave it as "void*", then make your function take a "void*".
                  I meant the latter - the data won't be altered.

                  Comment

                  • hzmonte@hotmail.com

                    #10
                    Re: Dilemma of const-ifying a function parameter

                    Bill Pursell wrote:
                    I would go with the former, but a slight modification
                    of the second solution is to provide an accessor function
                    which casts away the const from the structure. eg
                    void *ret;
                    ret = (void *)(i->elem)
                    This has essentially exactly the same problems you
                    describe, but it localizes the cast.
                    After reading all the related posts in this newsgroup (it turns out
                    that there are other threads on this subject - just search for "const
                    poisoning") so far I have felt that this is the most satisfactory
                    solution.

                    Comment

                    • Michael Mair

                      #11
                      Re: Dilemma of const-ifying a function parameter

                      hzmonte@hotmail .com schrieb:
                      >>Here you are going wrong.
                      >
                      Where did I go wrong? I did make a mistake by saying constifying elem;
                      what I meant was constifying *elem. That is, the data pointed to by
                      the pointer is not changed.
                      You snipped a little bit too much context:
                      - as I understood you, you wanted to store a copy of a const void *
                      which itself was not to be const void * but void *; afterwards,
                      outside the function where you stored the copy, you wanted to modify
                      the storage through this copy.
                      - As the compiler correctly did give you a diagnostic, you
                      complained that this seems wrong.
                      - It seemed to me that you were not aware of const semantics

                      Thus my statement.
                      >>Make[*]
                      > const void *elem
                      >>if you want to never ever modify *elem via i->elem
                      >>and never to be able to modify *(i->elem) when you get
                      >>the address from i->elem instead of another copy of the
                      >>address.
                      >>Then "const"ing *elem via "const void *elem" makes sense.
                      >
                      I do want to modify *elem outside this (enqueue) function. That is why
                      I said I have a dilemma.
                      Okay.
                      >>There is a difference between "the pointer parameter will not be
                      >>used in the function to modify what the pointer points to" and
                      >>"the address passed via the pointer parameter and its copies made
                      >>during the function's lifetime are not used to modify what the
                      >>pointer points to". You essentially promise the latter when saying
                      >>"const void *elem".
                      >
                      Yes, I want that (latter) promise.
                      Okay.
                      >>Once again: const-ifying elem means "void * const elem" and that
                      >
                      Again, I should have said "const-ifying *elem".
                      >
                      >>the parameter will not change its value. If you want to be able
                      >>to say "the value elem points to is not changed but the value
                      >>i->elem points to may be changed" then you have also to pass the
                      >>size of *elem and create a copy of *elem:
                      > i->elem = malloc(size);
                      > if (i->elem == NULL) {
                      > /* Your sensible action here */
                      > }
                      > memcpy(i->elem, elem, size);
                      >
                      It would be more precise to say that what I want is "the value elem
                      points to is not changed within the function but the value i->elem
                      points to may be changed outside the function".
                      Your suggestion resolved the problem but requires additional memory
                      that is not necessary had C provided a way to get around this dilemma.
                      There is no real need to change *elem in the function and thus no need
                      to malloc a copy of *elem - the need arises only because C does not
                      satisfy both goals (type compatibility and the desirability to
                      represent a parameter as "does not change" in a function) at the same
                      time.
                      True; however, this is the only conceptually clean solution
                      I know. With using "const void *" and 'casting away' const,
                      you always risk trying to modify a truly const object that
                      somehow got in there...


                      Cheers
                      Michael
                      --
                      E-Mail: Mine is an /at/ gmx /dot/ de address.

                      Comment

                      • hzmonte@hotmail.com

                        #12
                        Re: Dilemma of const-ifying a function parameter

                        Michael Mair wrote:
                        - As the compiler correctly did give you a diagnostic, you complained that this seems wrong.
                        I am not saying the compiler is wrong. I am just saying that the C
                        language design seems to be caught in two conflicting goals:
                        1. Type safety: a "const type *" cannot be assigned to a "type *", for
                        example. A cast is needed to subvert it.
                        2. Invariance safety: A parameter that is not modified in a function
                        can be qualified with 'const' and enforced by the compiler.
                        Both goals are desirable. And the compiler correctly gives me a
                        diagnostic when I violate the type safety rule. I am just looking for
                        a way that I can program it in a way that both goals can be satisfied
                        at the same time.
                        True; however, this is the only conceptually clean solution
                        I know. With using "const void *" and 'casting away' const,
                        you always risk trying to modify a truly const object that
                        somehow got in there...
                        OK, then let me say this: There are at least 3 ways to do it, each has
                        its benefits and drawbacks:
                        1. Not using 'const' at all: the drawback is that there is no
                        "invariance safety" (I coined this term).
                        2. Casting away the 'const': type safety is subverted.
                        3. Making a copy of the data: Additional copying and memory is needed.

                        Comment

                        • hzmonte@hotmail.com

                          #13
                          Use of const to qualify a function parameter

                          To facilitate discussion, a copy of the "Rationale for International
                          Standard - Programming Languages -C Revision 5.10 April-2003" can be
                          found
                          at:http://www.open-std.org/jtc1/sc22/wg...onaleV5.10.pdf :
                          <quote>
                          Clause 6.7.3 Type qualifiers:
                          The C89 Committee added two type qualifiers, const and volatile ...
                          Individually and in combination they specify the assumptions a compiler
                          can and must make when accessing an object an lvalue.
                          The syntax and semantics of const were adapted from C++; the concept
                          itself has appeared in other langauges. ...
                          Type qualifiers were introduced in part to provide greater control over
                          optimization. ...
                          The basic qualifiers can be characreized by the restrictions they
                          impose on access and cacheing:
                          const - No writes through this lvalue. In the absence of this
                          qualifier, writes may occur through this lvalue.
                          .... const is specified in such a way that an implementation is at
                          liberty to put const objects in read-only storage, and is encouraged to
                          diagnose obvious attempts to modify them, but is not required to trace
                          down all the subtle ways that such checking can be subverted.
                          </quote>
                          1. The document does not explicitly address the use of 'const' to
                          qualify a parameter of a fucntion. I am not able to find any rationale
                          for using 'const' in the C++ Standard either. Maybe the justification
                          is buried in some committee minutes.
                          2. Though many people cite the desirability of making clear the fact
                          that the parameter is not changed in the function if it is
                          const-qualified, from the standpoint of safety and maintainability ,
                          this benefit is not mentioned in the document as a rationale for
                          including 'const' in the C syntax. And there seems to be different
                          opinion as to the feasibility/effectiveness of optimizing the code when
                          a parameter is const-ified.

                          I am starting to think that maybe const, as used to qualify a function
                          parameter, cannot be used to satisfy two goals (type safety and
                          invariance safety) at the same time. Perhaps two different constructs
                          are needed to satisfy two different goals. For example, a new keyword
                          'in' - like the one used in Ada to designate an input parameter, is
                          needed to designate a parameter that is not changed in a function. So
                          in my enqueue() example, the function would be like this:
                          void enqueue(t_fifoQ ueue *q, in void *elem)
                          {
                          t_fifoItem *i, *last;

                          i = malloc(sizeof(t _fifoItem));
                          i->elem = elem;
                          last = q->prev;
                          last->next = i;
                          i->next = q;
                          q->prev = i;
                          }
                          And 'in' does not play a part in type compatibility; so "i->elem =
                          elem" is okay.
                          (And 'in' is not meant to solve the 'const poisoning' problem - that's
                          a separate issue, involving whether the benefit of doing it is worth
                          the trouble of cascading its use.)

                          For those interested, I found many other threads in this newsgroup that
                          are related to this subject - I never thought this is such a hot topic.
                          They are with the subjects "Whats the deal with 'const'?" " to const
                          or not to const " "is const useless and dangerous?" "is const useless
                          and dangerous?" "ptr to const params to show "read only"" "function
                          (const char *) vs. function (char *)" "compatible types in assignment"
                          "problem with constant pointer...". Most, if not all, of these
                          discussed the usefulness of 'const' (the last discussion thread raises
                          question of whether casting away the 'const' would induce undefined
                          behavior) but stopped short of really identifying the conceptual issue
                          to my satisfaction (not the practical issue of cascading the const).
                          (There is yet another lengthy discussion thread with the subject "What
                          Value Does Const Correctness Offer" in a C++ forum at
                          http://www.cpptalk.net/what-value-do...r-vt11516.html.)

                          Comment

                          • hzmonte@hotmail.com

                            #14
                            Re: Dilemma of const-ifying a function parameter

                            Let me refine my arguments. Two means, among others, to ensure safety
                            are:
                            1. Immutability on assignment - A variable of type "const float *" is
                            not assignable to a variable of type "float *", for example.
                            2. Immutability on parameter passing - A parameter qualified with
                            'const' is not modifiable inside a function.
                            Apparently there needs to be two different constructs to achieve these
                            means. Using the same 'const' to achieve them creates conflicts.

                            Comment

                            • Michal Nazarewicz

                              #15
                              Re: Use of const to qualify a function parameter

                              hzmonte@hotmail .com writes:
                              Perhaps two different constructs are needed to satisfy two different
                              goals. For example, a new keyword 'in' - like the one used in Ada
                              to designate an input parameter, is needed to designate a parameter
                              that is not changed in a function. So in my enqueue() example, the
                              function would be like this:
                              void enqueue(t_fifoQ ueue *q, in void *elem) {
                              t_fifoItem *i, *last;
                              >
                              i = malloc(sizeof(t _fifoItem));
                              i->elem = elem;
                              last = q->prev;
                              last->next = i;
                              i->next = q;
                              q->prev = i;
                              }
                              Ugh? How would it differ from:

                              void enqueue(t_fifoQ ueue *q, void *const elem) {

                              Secondly, I don't see the use of the keyword "in" or "const" in such
                              situation. As a programmer who uses given function, I do not care if
                              function changes the value of argument or not since the changes don't
                              affect outside of the function.

                              --
                              Best regards, _ _
                              .o. | Liege of Serenly Enlightened Majesty of o' \,=./ `o
                              ..o | Computer Science, Michal "mina86" Nazarewicz (o o)
                              ooo +--<mina86*tlen.pl >--<jid:mina86*jab ber.org>--ooO--(_)--Ooo--

                              Comment

                              Working...