Author Topic: Limiting fixed values that can be directly assigned to a variable at compile tim  (Read 1821 times)

0 Members and 1 Guest are viewing this topic.

Offline ricko_ukTopic starter

  • Super Contributor
  • ***
  • Posts: 1015
  • Country: gb
Hi,
is there a way to limit the values that can be assigned to a variable so that if some other "fixed/numerical/const" values are assigned in the code the compiler gives error.

Something like enum... but enum allows the variable to be assigned other values too.

I am thinking, for example, of state machine cases where you want to make sure only those values you defined can be assigned. That is just one of various other cases where such functionality would be useful.

Is that possible in C? If not, what about c++?

Thank you :)
 

Online Psi

  • Super Contributor
  • ***
  • Posts: 9889
  • Country: nz
Something like enum... but enum allows the variable to be assigned other values too.

Not quite sure how you mean?  typedef enum?
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline woofy

  • Frequent Contributor
  • **
  • Posts: 321
  • Country: gb
    • Woofys Place
Do you mean something like:

Code: [Select]
#define x 10
#if x>21 || x<5
#error "Go RTFM"
#endif

Offline ricko_ukTopic starter

  • Super Contributor
  • ***
  • Posts: 1015
  • Country: gb
sorry, I didn't explain it properely.

Lets say I want variable VarA to be assigned only the values Monday, Tuesday and Friday.

So that this code will compile:

VarA = Monday;

But these ones would not:
VarA = Wednesday;
VarA = 27;

Thank you
 

Online Psi

  • Super Contributor
  • ***
  • Posts: 9889
  • Country: nz
Declare VarA from a typedef enum
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline ricko_ukTopic starter

  • Super Contributor
  • ***
  • Posts: 1015
  • Country: gb
Thank you,
that does not work. This code compiles ok:

typedef enum {
    Mon = 0,
    Tue,
    Wed
} myTypeDef;

main()
{
     myTypeDef myVar;
     myVar = 14;
     output(myVar)
}

As mentioned, I am looking for a solution that if I assign to VarA any value other than 0, 1, 2, Mon, Tue or Wed it would not compile. But in the code above, I can assign any numerical value to VarA (10, 200, etc) and it compiles.
« Last Edit: April 18, 2021, 12:20:52 pm by ricko_uk »
 

Offline Tagli

  • Contributor
  • Posts: 31
  • Country: tr
Normally it shouldn't compile when using C++ unless you explicitly cast it. But I'm not sure about C. Type checking / type safety is more strict in C++ than it's in C.
Gokce Taglioglu
 

Online rstofer

  • Super Contributor
  • ***
  • Posts: 9886
  • Country: us
Pascal handles this with enumerated variables and strong typing:

https://www.tutorialspoint.com/pascal/pascal_variable_types.htm
https://wiki.freepascal.org/Enum_Type

Not useful information I suppose.
 

Offline mjkuwp

  • Supporter
  • ****
  • Posts: 259
  • Country: us
  • mechanical engineering defector
    • The Mz Lab
I have never used either of these things.  my first thought was an assert but #error seems to work and is straightforward.

#define ABC 0x2

#if !defined ABC || ABC < 0x7F
#error ABC is too small
#endif

 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3137
  • Country: ca
When you assign a value to a variable in C, there's no distinction whether the value is known at compile time (I guess this is what you mean by "constant") or not. So, what you want to do is impossible.

You certainly can use macros, but these are likely to break if you try to assign something which is not known during pre-processing (which is the very first step of the compilation).

My advice is simple. Don't clutter your mind with inconsequential. Admittedly, there are other things you should concentrate on during programming. If instead you concentrate on how to protect yourself from yourself, you're just wasting your time.
 

Online Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11534
  • Country: my
  • reassessing directives...
Is that possible in C? If not, what about c++?
i dont have time to test it but... according to this... https://stackoverflow.com/questions/21683289/enum-in-c-is-not-throwing-an-error-on-invalid-input use..
Code: [Select]
enum class Traffic_light {red, yellow, green};
enum class Warning {green, yellow, orange, red};

Warning w = 1; // error. no int -> Warning implicit conversion
Traffic_light t = Warning::red; // type error. Warning::red is a different type

what are you trying to catch? bad coding practice such as in your example myTypeDef myVar = 14; ? another way for me to catch invalid value (during runtime) is using operator overloaded class... ymmv.
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8106
  • Country: fi
C is quite permitting language, you simply can't catch all types of programmer mistakes.

(With -Wall enabled, C is still quite good! It has many great safety ideas not present in trendy modern-day languages, such as static typing with explicit and implicit casting rules and quite good warning system implemented by all modern compilers, explicit declaration of variables preventing new variable from being generated from a typo, and so on...)

C catches the actually probable errors
day = Thurday;

or

doy = Thursday;

just fine. The design feature of having interchangeable int and enum types may not have been the greatest idea, but is there a possibility you could control yourself (and/or your team) so that you don't do
weekday = Thursday;
somewhere and then
weekday = 42;
somewhere else?

It seems to me this is quite improbable source of error. Keep your variable naming and code quality in check otherwise and such assignment hurts the reader's eye immediately.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14297
  • Country: fr
I remember the C vs. C++ enums were already discussed a while ago. In C, there is no check whatsoever. In C++, there is.

So in C, there is no way of doing this. In C++, you can use enums - assigning values not in the enum in C++ should yield an error.

For state machines, obviously in C you can use a fallback case - 'default' - which would then handle the error if it ever gets there. But it's only a run-time check. I can't see a simple way of checking that at compile time.

But as said above, what's the probability of this being a real problem? When dealing with state machines, as in your example, do you think the probability of assigning a value (here, a state, for instance) not in the set of handled states would be any higher than just assigning the wrong state (in the right set of handled states)? It is doubtful. And in any case, a check at run time looks like a good idea.

Now for the more general case of restricted range variables, few languages really allow you to define static types with restricted ranges that are checked at compile time. One of them is ADA. C++ is better than C here, but only for the limited case of enums, which is better than nothing but is still very lacking.

 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11790
  • Country: us
In C++ a class is nothing other than a user defined type. So if you want to create a type that only accepts certain defined values and rejects others, then you can do that in C++. But as NorthGuy said, you might be wasting your time creating a class to solve such a trivial problem. Classes are better suited to types that have specific behaviors of their data that you want to model.
 

Offline ricko_ukTopic starter

  • Super Contributor
  • ***
  • Posts: 1015
  • Country: gb
Thank you all for the detailed infos and explanations! :)
 

Offline lucazader

  • Regular Contributor
  • *
  • Posts: 221
  • Country: au
You can use this site to quickly play around with test code to see what will and will not be caught by the compiler at compile time.

https://gcc.godbolt.org/z/s9KePWq3b
In the example above you can see that the c++ code will have a compile time error on the implicit conversion from int to the custom enum.
 

Offline ricko_ukTopic starter

  • Super Contributor
  • ***
  • Posts: 1015
  • Country: gb
Thank you lucazader,
that link is handy! :)
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 21606
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Enums are used for bit flags as well (or at least, I've seen and used them that way before), makes sense that there'd be no arithmetic checking.  Enum type is just int, if not otherwise specified.

Would an assert help any?  That's a runtime fail I think, maybe not the most helpful.

Otherwise, doing it in preprocessor, your options are limited I think.  You'd have to write a macro for the type, and for reading/assigning it, I think?  I've seen some really well made macros, that effectively make an improved, more advanced type system, unfortunately I don't remember them by name...

Or if you want to get deep into compiler internals, you could perhaps write a plugin to check assignment at the AST level... good luck with that.

Not sure if there's something inbetween that I'm just not aware of... read the compiler docs?  What's on StackExchange?

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11790
  • Country: us
Not sure if there's something inbetween that I'm just not aware of...

Well, C++ would do it. If you define a user-defined type (as a class), then the compiler will check and complain if you try to initialize with, or assign something the class is not designed to accept.

But it's a lot of work to do this unless you have other, stronger reasons to do it.
 

Offline T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 21606
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Yeah, like I said, in... wait, as I should've said: in C. :D

There's a certain minimal amount of work to use C++, maybe not a big deal just for like, classes, without committing to all the new syntax and templating and all that that's available.

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline Tagli

  • Contributor
  • Posts: 31
  • Country: tr
When using C++, you don't need to implement classes for this. You don't even need the new enum class. Classical enums will do.

Using classical enums:
Code: [Select]
enum Number {Zero = 0, One = 1, Two = 2};
Number n0 = Zero; // OK, as expected
Number n1 = 1; // Compile error
Number n2 = static_cast<Number>(2); // Explicit casting, works OK.
Number n3 = static_cast<Number>(3); // This one works too.
int i0 = n0; // OK, implicit casting when converting back to int.

Using new enum classes:
Code: [Select]
enum class Number {Zero = 0, One = 1, Two = 2};
Number n0 = Zero; // Compile error
Number n1 = Number::One; // OK, as expected
Number n2 = static_cast<Number>(2); // Explicit casting, works OK.
Number n3 = static_cast<Number>(3); // This one works too.
int i1 = n1; // Compile error
int i2 = static_cast<int>(n2); // Works OK.
Gokce Taglioglu
 
The following users thanked this post: oPossum, newbrain, lucazader

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3683
  • Country: us
As everyone else has explained, that just isn't the way enums work in standard C.

This could be implemented by a linter.  For instance you could completely outlaw assigning an enum variable from an integer literal, requiring you to always use the enum names for constant assignment.  I don't know of any examples of this, it's likely that use of the integer formats are too widespread for such a lint rule to be useful.
 

Offline szszoke

  • Regular Contributor
  • *
  • Posts: 92
  • Country: se
As far as I know this is not possible in C and in C++ you would need to use an enum class.

I work with TypeScript in my day job and there it is possible to create a union of literal types.

A literal type is only capable of holding a single value.

You can then create a union of several literal types.

When you assign a variable of a union type, the value can be something that is assignable to one of the types in the union.

Example:

type Foo = "foo"; // literal type, can only hold "foo"
type Bar = "bar"; // literal type, can only hold "bar"

type MyType = Foo | Bar; // union type, can hold a Foo or a Bar

const a: MyType = "foo"; // ok
const b: MyType = "bar"; // ok
const c: MyType = "test"; // compilation error
const d: MyType = "123; // compilation error
const e: MyType = null; // compilation error
const f: MyType = undefined; // compilation error

const myStr: string = a; // ok

I realize that this might not be usable for you but I think it is a cool thing that can be done and it worth sharing.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf