A better solution? De-Const'ing a variable

  • Thread starter S James S Stapleton
  • Start date
S

S James S Stapleton

I have some code, and I want to make it future-resistant. I have a bunch of
variables that are set up run-time, and once set up, should act as
constants. I don't want to #define them, because their values are determined
during the execution of the initialization routines, so I made them const
(I'd rather making it hard for a dev to accidentally overwrite the values).
The problem is, they are being set run-time by the return of a function
call. Right now my solution is ugly, and required me to turn a .c file to a
..cpp file. Any suggestions on how to do this in a better manner? I couldn't
find it via google or my reference books.

Basically I'm trying to make a variable non-const for one statement.

ex:
in the .h
extern const type_id CHAR_TYPE;

in the .cpp which I'd rather be a .c
const type_id CHAR_TYPE;
/*...*/
void mylib_init()
{
/*note the addressing and deadressing ops, const_cast requries pointers
to work...*/
*(const_cast<type_id*>(&CHAR_TYPE)) = ctypeless_register(sizeof(char),
NULL, NULL, "char");
}


so, if you are like me, you see heaploads of problems with my solution. Any
suggestions for a fix that is (a) less ugly, (b) doesn't require the use of
C++, and (c) makes sure people who use the code get warnings/errors if they
try to change the values of these variables.

Thanks,
-Jim Stapleton
 
S

S James S Stapleton

Two approaches I've seen used for "final" global variables:

/* foo.h */
int getGlobal(void);
#define global getGlobal()

/* foo.c */
#include "foo.h"
static int actualGlobal;
void init_foo(void) {
actualGlobal = whatever;
}
int getGlobal(void) {
return actualGlobal;
}

Ah, that is a nice solution.

The other may be a hair more efficient, but is also a touch
less bullet-proof:

actually, I'm not sure on the efficiency part. If the library is not
dynamically loaded, wouldn't this be auto-inlined via the compiler? And if
the compiler isn't smart enough, could you add an inline anyway? That would
make this as efficient as a straight variable access. However, for dynamic
linkage, the other method would be more efficient (follow a pointer +
variable access instead of the overhead of calling a function).

Thanks,
-Jim Stapleton
 
K

Kaz Kylheku

I have some code, and I want to make it future-resistant. I have a bunch of
variables that are set up run-time, and once set up, should act as
constants. I don't want to #define them, because their values are determined
during the execution of the initialization routines, so I made them const
(I'd rather making it hard for a dev to accidentally overwrite the values).
The problem is, they are being set run-time by the return of a function
call. Right now my solution is ugly, and required me to turn a .c file to a
.cpp file.

Although C++ allows for a file scope const to be initialized by the return
value of a function call, such a constant isn't a true compile-time constant.
You can't use it as a label in a switch statement, array size, etc.
Any suggestions on how to do this in a better manner?

I would just ditch const. You can save a copy of the value
in a ``secret'' variable, and at various times during the
execution of the program, assert that the two are equal.
I couldn't
find it via google or my reference books.

Basically I'm trying to make a variable non-const for one statement.

ex:
in the .h
extern const type_id CHAR_TYPE;

in the .cpp which I'd rather be a .c
const type_id CHAR_TYPE;

An uninitialized const is invalid C++!
/*...*/
void mylib_init()
{
/*note the addressing and deadressing ops, const_cast requries pointers
to work...*/
*(const_cast<type_id*>(&CHAR_TYPE)) = ctypeless_register(sizeof(char),
NULL, NULL, "char");
}

Uh, this isn't correct C++ either. You can't cast away the constness of a const
object in C++ assign to that object.

You're depending on the fact that the translation units of the program are
dealt with separately, so that the extern declared const object's value cannot
be treated as a compile-time constant.

If this ``solution'' is acceptable to you, you can do exactly the same hack in
C, except that instead of const_cast, you have to use a C cast.

The way to do this in C++ is:

// in the header
extern const type_id CHAR_TYPE;

// in the cpp
extern const type_id CHAR_TYPE = ctypeless_register(...);

C++ allows non-constant expressions to be used as the initializers of
static objects. You may run into into initialization problems. Obviously,
this code is going to be executing prior to mylib_init, and in fact
prior to main. It is arbitrarily ordered with respect to other
global initializations for other modules.

Another thing, const_cast doesn't require pointers. I suspect that can do it
with references:

const_cast<type_id &>(CHAR_TYPE) = ...

The following C++ translation unit compiles fine under gcc 3.4.3:

const int x = 3;

int main()
{
const_cast<int&>(x) = 4;
return 0;
}

But it crashes:

(gdb) r
Starting program: /home/kaz/export/a.out

Program received signal SIGSEGV, Segmentation fault.
main () at refcast.cc:5
5 const_cast<int&>(x) = 4;

The const is actually allocated in write-protected virtual memory.
(This has nothing to do with the reference cast; using pointers makes no
difference). So your const hack is not portable to this platform.
 
S

S James S Stapleton

/* foo.h */
int getGlobal(void);
#define global getGlobal()

/* foo.c */
#include "foo.h"
static int actualGlobal;
void init_foo(void) {
actualGlobal = whatever;
}
int getGlobal(void) {
return actualGlobal;
}

I just thought of something from this, make it a non-valid lvalue without
the function overhead:

#define MY_GLOBAL (MY_HIDDEN_GLOBAL | 0)

This should have less overhead than a function, and not be 'editable',
correct? Any comments on it as opposed to the pointer method?

Thanks,
-Jim Stapleton
 
B

Ben Bacarisse

S James S Stapleton said:
I just thought of something from this, make it a non-valid lvalue without
the function overhead:

#define MY_GLOBAL (MY_HIDDEN_GLOBAL | 0)

This should have less overhead than a function, and not be 'editable',
correct? Any comments on it as opposed to the pointer method?

Writing + 0 works for more types than | 0, but even then not for all.
You can get closer with the rather odd:

#define MAKE_NAME(n) global_const_##n
#define GLOBAL(name) (1 ? MAKE_NAME(name) : MAKE_NAME(name))

since (in C) the result of ?: is not an lvalue (but not in C++).

Personally, I would just extend Eric's pointer idea to a struct:

struct constants {
int zero;
double pi;
/* ... */
};

const struct constants *constant;

and have your initialisation set the pointer once a (non-const) static
struct has been written.
 
S

S James S Stapleton

Writing + 0 works for more types than | 0, but even then not for all.
You can get closer with the rather odd:

#define MAKE_NAME(n) global_const_##n
#define GLOBAL(name) (1 ? MAKE_NAME(name) : MAKE_NAME(name))

since (in C) the result of ?: is not an lvalue (but not in C++).

This is only for integer values, and I plan on the code being usable in C or
C++. Actually, that's why I'm trying to keep this as pure-C as possible, I'd
like it to be trivially usable by anyone, as possible.

Even the people who've chosen to embrace D.

-Jim
 
B

Ben Bacarisse

S James S Stapleton said:
This is only for integer values, and I plan on the code being usable in C or
C++. Actually, that's why I'm trying to keep this as pure-C as possible, I'd
like it to be trivially usable by anyone, as possible.

OK. I still recommend a pointer to const struct!
 
S

S James S Stapleton

This is known as "security through obscurity." Note that
MY_HIDDEN_GLOBAL needs to be known by name and be accessible
everywhere MY_GLOBAL is used, so its optimistic name is just
an expression of hope.

If you can trust the programmers to leave MY_HIDDEN_GLOBAL
alone, why not just trust them not to abuse MY_GLOBAL, and be
done with all the running around? Contrariwise, if you can't
trust them to keep their hands of MY_GLOBAL, why do you suppose
they'll respect the privacy of MY_HIDDEN_GLOBAL?

I'm not doing this as a matter of security, more along the lines of
politeness. MY_HIDDEN_GLOBAL will have a name unlikely to be used
accidentally. MY_GLOBAL, since it's being used, might accidentally get
assigned somewhere. This is just to help with debugging issues.
It seems to me you're seeking a technical solution to what
may be a social and behavioral problem.

Sort-of. I just want to make sure that accidental errors can be more easily
traced/debugged.

-Jim Stapleton
 
L

Linonut

* S James S Stapleton peremptorily fired off this memo:
Ah, that is a nice solution.

Why not just:

/* foo.h */

extern int getGlobal (void);

/* foo.c */

#include "foo.h"

int
getGlobal (void)
{
static int actualGlobal = whatever;
return actualGlobal;
}
 
L

Linonut

* Eric Sosman peremptorily fired off this memo:
This assumes the `whatever' can be calculated at compile
time, but the O.P. said he wanted a value that would not be
available until run-time.

Doh!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top