views:

103

answers:

3

How do I force const-ness of the memory pointed to by obj->val1 in the function fn?

#include <iostream>

struct foo {
    int* val1;
    int* val2;
    int* val3;
};

void fn( const foo* obj )
{
    // I don't want to be able to change the integer that val1 points to
    //obj->val1 = new int[20]; // I can't change the pointer,
    *(obj->val1) = 20; // But I can change the memory it points to...
}

int main(int argc, char* argv[])
{
    // I need to be able to set foo and pass its value in as const into a function
    foo stoben;
    stoben.val1 = new int;
    *(stoben.val1) = 0;
    std::cout << *(stoben.val1) << std::endl; // Output is "0"
    fn( &stoben );
    std::cout << *(stoben.val1) << std::endl; // Output is "20"
    delete stoben.val1;
    return 0;
}

The code here is pretty self explanitory. I need to be able to make a non-const object and fill it with data, but then pass it to a function where this data cannot be modified. How can I go about this?

I know I can just pass in a const int pointer, but theoretically, this class contains several other pointers which I will need in "fn" as well.

Thanks,

Griff

+4  A: 

Since you tagged as C++, you could make the member private and make an accessor that returns a const int *. You could originally set the member via your constructor or a friend function.

Brian R. Bondy
Ahh. I forget that structs in C++ can have methods and member variables. That'll solve my problem, though just for giggles, is it possible at all to do this in C?
Griffin
@Griffin: If you really wanted to make sure you would probably pass it in separately.
Brian R. Bondy
@Griffin: yes. See my answer, which is valid for both C and C++.
Ben Collins
@Ben: No that is wrong.
Brian R. Bondy
Alas, I find myself unable to accept this answer - the first one - as the accepted answer. Stack Overflow presents an error message when I tick the checkmark.
Griffin
Ah! There it goes.
Griffin
@Brian R. Bondy: yes, I know. I misread the question; I deleted my answer.
Ben Collins
+2  A: 

You really can't. A const foo specifies that the members inside are const, that is, they are constant pointers to integers, not pointers to constant integers.

The proper solution to this would be via encapsulation, hiding these members and providing a public interface. A practical solution, should you be forbidden to modify the struct foo, would be through private inheritance:

struct foo {
    int* val1;
    int* val2;
    int* val3;
};

struct constFoo : private foo {
public:
   const int* getVal1() { return val1; }
   const int* getVal2() { return val2; }
   const int* getVal3() { return val3; }
};

Of course, you would need to create the appropriate constructors, etc., so that the original foo can be set up.

Shirik
Seems that I'll have to add some extra complexity to my poor little struct. Thanks for the informative code sample!
Griffin
+1  A: 

I'm not a C++ person, but in C, I'd handle this through two different struct declarations, one public, one private:

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

struct private_foo {
    int* val1;
    int* val2;
    int* val3;
};

struct public_foo {
    int const * const val1;
    int const * const val2;
    int const * const val3;
};


void fn( struct public_foo * obj )
{
    int local;
    *(obj->val1) = 20; // compile error
    obj->val1 = &local; // compile error
}

int main(int argc, char* argv[])
{
    // I need to be able to set foo and pass its value in as const into a function
    struct private_foo stoben;
    stoben.val1 = malloc(sizeof(int));
    if (!stoben.val1) { return -1; }
    *(stoben.val1) = 0;
    printf("%d", *(stoben.val1) );
    fn( (struct public_foo *) &stoben );
    printf("%d", *(stoben.val1) );
    free(stoben.val1);
    return 0;
}

When I try to compile the above w/ GCC, I get the following compiler errors, since I'm trying to modify read-only memory:

temp.c: In function ‘fn’:
temp.c:20: error: assignment of read-only location
temp.c:21: error: assignment of read-only member ‘val1’
rampion
Oooo.. Now that's clever. Kudos to you, sir. I'm adding this to my bag of tricks.
Griffin