Thread: deep const correctness

  1. #1
    Registered User
    Join Date
    Oct 2001
    Posts
    2,129

    deep const correctness

    I want to pass a pointer to a structure to a function, and I want to make sure not to modify one or some of the members of the structure, on a per-member basis. Can I use a language construct, like const, to specify that?

  2. #2
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    One non-pretty solution I can think of is:
    Code:
    struct foo
    {
        int x, y;
    };
    
    struct const_foo
    {
        const int x;
        int y;
    };
    
    void myfunc(const_foo* p) {}
    
    int main()
    {
        foo my_foo = { 10, 10 };
        myfunc((const_foo*)&my_foo);
    }
    Disclaimer: the code is not meant to compile.
    Is this like something you want to do?
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  3. #3
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    No, in C, const is per parameter to a function or per actual variable, so you either make the entire struct const (in which case you can't modify it) or don't (in which case you CAN modify it).

    In C++, you could pass a constr struct to a function, and use mutable to allow certain member to still be modifiable - however, this should be used with utter care - if you have more than a couple fo mutable members, you are probably doing something wrong.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  4. #4
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    So it doesn't work in C? Stupid language...
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  5. #5
    Registered User
    Join Date
    Oct 2001
    Posts
    2,129
    Yes, something like that, but my concern with that is that I don't think that const and non-const types are compatible with each other (see 6.7.3.9), meaning using a struct through a pointer to a struct where one member is const qualified would not be guaranteed to work.

  6. #6
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Elysia View Post
    So it doesn't work in C? Stupid language...
    Actually, my comment wasn't to your post, but to the original post.

    Your code compiles - but it's absolutely not the right thing to do, since it relies on the programmer to get the right variant of the struct at any given time.

    However, if there are elements in the struct that are initialized by an initializer list (like my_foo in the example above), that have values that never change, then those can be made const in the struct. If most or all values are changed (or initialized by assigning a value to the struct itself), but only some are changed in some functions, then the const inside the struct doesn't work.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I suppose you can pass pointers to the individual members of the struct and make the pointers pointers-to-const as appropriate.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by matsp View Post
    Actually, my comment wasn't to your post, but to the original post.
    Oh!

    Your code compiles - but it's absolutely not the right thing to do, since it relies on the programmer to get the right variant of the struct at any given time.
    No, it doesn't compile in C I left out the typedef and struct keyword on purpose to mock C
    I agree it's a very hackish and bad approach, which I did mention, but I see no alternative...

    Quote Originally Posted by laserlight View Post
    I suppose you can pass pointers to the individual members of the struct and make the pointers pointers-to-const as appropriate.
    Does that not just defeat the whole purpose of a struct, though?
    Perhaps you could pass the certain members inside the struct that must not be changed as pointers to const or vice versa if your struct contains mostly const.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  9. #9
    Registered User
    Join Date
    Oct 2001
    Posts
    2,129
    Mats:

    Even mutable would not work, since I want to make sure I don't modify the member in the function, and mutable is defined in the class. Conceivably, there would be another function that has const applied to a different set of members.

    laserlight:

    Yes, I could do that, but unfortunately, it throws the entire concept of data aggregation and encapsulation out the window.

  10. #10
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by robwhit View Post
    Mats:

    Even mutable would not work, since I want to make sure I don't modify the member in the function, and mutable is defined in the class. Conceivably, there would be another function that has const applied to a different set of members.

    laserlight:

    Yes, I could do that, but unfortunately, it throws the entire concept of data aggregation and encapsulation out the window.
    Then I think Elysia's solutin of passing only certain members to the function is the only workable one.

    Another option is of course to build a "per-funciton test-framework", where you pass a range of data to each function, and check that the function hasn't modified something it shouldn't modify. Just make sure that you cover all branches in the function (code-coverage tools will help here).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  11. #11
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by robwhit
    Yes, I could do that, but unfortunately, it throws the entire concept of data aggregation and encapsulation out the window.
    Yes, but you have already thrown aggregation and encapsulation (and abstraction) out of the window by considering the internals and trying to separate how they can be modified.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  12. #12
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by robwhit View Post
    I want to pass a pointer to a structure to a function, and I want to make sure not to modify one or some of the members of the structure, on a per-member basis. Can I use a language construct, like const, to specify that?
    You could create a wrapper class which proxies for the underlying object and only allows access to some of the members.

    Code:
    class A
    {
    public:
        int member_to_be_modified;
        int member_to_be_protected;
    };
    
    class AProxy
    {
    public:
        AProxy(A &object)
            : member_to_be_modified(object.member_to_be_modified),
              member_to_be_protected(object.member_to_be_protected)
        {
        }
    
        int &member_to_be_modified;
        const int &member_to_be_protected;
    };
    Of course this gets tedious if the class is large, or if it changes often.

    EDIT: Er, wait. This is C. Nevermind.
    Last edited by brewbuck; 12-11-2008 at 02:19 PM.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  13. #13
    Registered User
    Join Date
    Oct 2001
    Posts
    2,129
    Quote Originally Posted by laserlight View Post
    Yes, but you have already thrown aggregation and encapsulation (and abstraction) out of the window by considering the internals and trying to separate how they can be modified.
    Touche. But there is also the part about having an abstract interface, if not an implementation.
    Quote Originally Posted by matsp View Post
    Then I think Elysia's solutin of passing only certain members to the function is the only workable one.

    Another option is of course to build a "per-funciton test-framework", where you pass a range of data to each function, and check that the function hasn't modified something it shouldn't modify. Just make sure that you cover all branches in the function (code-coverage tools will help here).
    Funny you say that... I was wondering about this to help specify to the test tools what variables should or should not be changed in a function. I don't have a particular test tool in mind, though.

    Is this sort of specification capability par for the course with test tools?
    Last edited by robwhit; 12-11-2008 at 11:28 PM. Reason: sp

  14. #14
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    I have a GotW just for your example Elysia:
    http://www.gotw.ca/gotw/076.htm
    According to that example, your code falls under "Criminal #3: The Cheat". Basically assuming that the same holds true for C as well as C++, it's undefined behaviour.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  15. #15
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by robwhit View Post
    Touche. But there is also the part about having an abstract interface, if not an implementation.Funny you say that... I was wondering about this to help specify to the test tools what variables should or should not be changed in a function. I don't have a particular test tool in mind, though.

    Is this sort of specification capability par for the course with test tools?
    You could always write your own small(ish) test for this purpose. Something that is based on a datastructure like this, perhaps [I just made this up based on other stuff that does roughly this sort of thing):
    Code:
    struct tobetested
    {
        int x;
        char s[20];
        double d;
    };
    
    #define VALUE(basestruct, member, type, unchanged) { #member, offsetof(basestruct, member), sizeof(type), unchanged }
    struct Changes
    {
        char *name;
        size_t offset;
        size_t size;
        bool unchanged;
    }     
    struct Testcase
    {
        int caseNumber; 
        char *caseName;
        int (*func)(struct tobetested *tbt);
        struct Changes *changes;
    };
    
    struct Changes xOnlyChanges[] = 
    {
        VALUE(tobetested, x, int, false),
        VALUE(tobetested, str, char[20], true),
        VALUE(tobetested, d, double, true),
        { NULL }
    };
    
    struct Changes noChanges[] = 
    {
        VALUE(tobetested, x, int, true),
        VALUE(tobetested, str, char[20], true),
        VALUE(tobetested, d, double, true),
        { NULL }
    };
    
    #define TC(tcfunc, tcno, changes) { tcno, #tcfunc, tcfunc, changes }
    struct TestCase testcases[] = 
    {
         TC(testCase1, 1, xOnlyChanges),
         TC(testCase2, 2, noChanges)
    };
    The actual testing code would set up a structure that contains valid (or invalid) values for the "tobetested" struct. Then make a "original" copy of the struct, call the testfunction and compare the resulting struct. Using memcmp() to compare the size number of bytes at offset, and then check if "unchanged" is true when memcmp is non-zero - that's a fail.

    I'm sure there are more clever ways to do this, and if you are ALWAYS using gcc, you can use "typeof(member)" instead of passing the type in as a parameter to the VALUE macro.

    I once wrote a set of macros to define the contents structure as a table, and it would include the same file twice to build the data structures.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Polynomials and ADT's
    By Emeighty in forum C++ Programming
    Replies: 20
    Last Post: 08-19-2008, 08:32 AM
  2. Undefined Reference Compiling Error
    By AlakaAlaki in forum C++ Programming
    Replies: 1
    Last Post: 06-27-2008, 11:45 AM
  3. Drawing Program
    By Max_Payne in forum C++ Programming
    Replies: 21
    Last Post: 12-21-2007, 05:34 PM
  4. Certain functions
    By Lurker in forum C++ Programming
    Replies: 3
    Last Post: 12-26-2003, 01:26 AM
  5. Half-life SDK, where are the constants?
    By bennyandthejets in forum Game Programming
    Replies: 29
    Last Post: 08-25-2003, 11:58 AM