-1

My goal is to define a clean API for my library.
One of my function returns a pointer that shall not be modified with pointers arithmetic.
To do so at compile-time, I was planning on using the const keyword in the function prototype.

Here's a naive example:

int global_var = 12;

int* const access_global_var(){ return &global_var;}

int main(void) {
  int* const ptr = access_global_var();

  *ptr = 15;  //< Should be valid 
  ptr++;      //< Should be Invalid 
  return 0;
}

It works as expected: the compiler throws an error at ptr++;.

Problem:
When compiling with the -Wextra clang/gcc flag, I get the following warnings:

warning: 'const' type qualifier on return type has no effect

Is this warning correct? Is there something I'm missing? Is there a better way to achieve what I'm trying to do?
As warnings are treated errors in my project, this is, as you can expect, problematic.
Thanks

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
gberth
  • 467
  • 5
  • 13
  • 4
    You don't need to qualify return values. The variable you assign the return value to is where you need qualification. – Christian Gibbons May 14 '20 at 15:09
  • I agree that I don't need it. But is there a way to enforce that user will use const with its variable­? – gberth May 14 '20 at 16:08
  • Even if you could force the caller to assign the function value only to a `const`, effectively requiring `int * const x = access_global_var();`, the caller could then do `int *y = x;`. Think about what you are asking as if the function returned a simple `int`, not a pointer. Would there be any use in insisting that a caller could assign a return, value, say 3, only to a `const int x = 3;` and never be allowed to use their own `x` for anything else? They have to be able to use `x` in expressions, yes? Say `x+4`? That should not be `const`, too, should it?… – Eric Postpischil May 15 '20 at 01:13
  • … Suppose you could force a `const int x = 3;` and somehow propagate this to disallow `int y = x;`. But, if `x+4` is not also forced to be `const`, then they could write `x+0` to remove the `const`, thus doing `int y = x+0;`. Or, if you disallowed that, then `int y = x+4-4;`. Similarly, the pointer could be `int *x = access_global_var() + 1 - 1;`. Or `int *x = access_global_var + HardToEvaluateFunction();`, which would prevent the compiler from knowing the function returns zero. From a language design perspective, this request is not feasible. – Eric Postpischil May 15 '20 at 01:16

2 Answers2

4

The return type of the function

int* const access_global_var(){ return &global_var;}

does not make sense.

If you will remove the qualifier const in any case you may not write for example

++access_global_var();

because the function returns an rvalue.

But even if you have the qualifier const you may write

int *p = access_global_var();

and then

++p;

That is you may assign a constant value to a non-constant object.

So the compiler warns that the qualifier used in the return type does not make sense and may be removed without changing the ,logic.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I understand lvalues. The fact that you can write `int *p = access_global_var();` is the problem that I want to solve. – gberth May 14 '20 at 16:07
  • 1
    @gberth Why is it a problem? Why may not the user increase the pointer? He does not change the global variable by increasing the pointer. – Vlad from Moscow May 14 '20 at 16:10
0

If, for some reason, you really want to force this upon developers (who will have no problem getting around this restriction if they want to), you can always use a function-like macro to try to enforce this. If you want the macro to be specific to a particular global var, like in your example, it might look like this:

#define create_global_var_accessor(ACCESSOR) \
    int *const ACCESSOR = &global_var

int global_var = 12;

int main(void) {
    create_global_var_accessor(ptr);

    *ptr = 15;  //< Should be valid 
    ptr++;      //< Should be Invalid 
    return 0;
}

Otherwise if you want something more generic, you can pass in the target global var along with its type (or use GNU typeof extension):

#define create_global_var_accessor(TYPE, ACCESSOR, ACCESSEE) \
    TYPE *const ACCESSOR = &(ACCESSEE)

int global_var = 12;

int main(void) {
    create_global_var_accessor(int, ptr, global_var);

    *ptr = 15;  //< Should be valid 
    ptr++;      //< Should be Invalid 
    return 0;
}

That aside, I'll echo Vlad from Moscow in stating that I don't think this is a problem in need of a solution.

Christian Gibbons
  • 4,272
  • 1
  • 16
  • 29