Limiting fixed values that can be directly assigned to a variable at compile tim - Page 1
Forum section: electronics / general

author: ricko_uk
author_rank: Super Contributor
timestamp: April 18, 2021, 11:02:01 am
content: 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

author: Psi
author_rank: Super Contributor
timestamp: April 18, 2021, 11:21:45 am
content: Quote from: ricko_uk on April 18, 2021, 11:02:01 amNot quite sure how you mean? typedef enum?

author: woofy
author_rank: Frequent Contributor
timestamp: April 18, 2021, 11:27:14 am
content: Do you mean something like:Code:#define x 10#if x>21 || x<5#error "Go RTFM"#endif

author: ricko_uk
author_rank: Super Contributor
timestamp: April 18, 2021, 11:45:06 am
content: 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

author: Psi
author_rank: Super Contributor
timestamp: April 18, 2021, 12:04:55 pm
content: Declare VarA from a typedef enum

author: ricko_uk
author_rank: Super Contributor
timestamp: April 18, 2021, 12:18:19 pm
content: 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.

author: Tagli
author_rank: Contributor
timestamp: April 18, 2021, 12:41:35 pm
content: 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.

author: rstofer
author_rank: Super Contributor
timestamp: April 18, 2021, 03:01:37 pm
content: Pascal handles this with enumerated variables and strong typing:https://www.tutorialspoint.com/pascal/pascal_variable_types.htmhttps://wiki.freepascal.org/Enum_TypeNot useful information I suppose.

author: mjkuwp
author_rank: Unknown
timestamp: April 18, 2021, 03:41:29 pm
content: 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

author: NorthGuy
author_rank: Super Contributor
timestamp: April 18, 2021, 04:10:46 pm
content: 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.

author: Mechatrommer
author_rank: Super Contributor
timestamp: April 18, 2021, 04:14:17 pm
content: Quote from: ricko_uk on April 18, 2021, 11:02:01 ami 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-inputuse..Code:enum class Traffic_light {red, yellow, green};enum class Warning {green, yellow, orange, red};Warning w = 1; // error. no int -> Warning implicit conversionTraffic_light t = Warning::red; // type error. Warning::red is a different typewhat 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.

author: Siwastaja
author_rank: Super Contributor
timestamp: April 18, 2021, 04:27:16 pm
content: 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 errorsday = Thurday;ordoy = 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 doweekday = Thursday;somewhere and thenweekday = 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.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 18, 2021, 04:59:18 pm
content: 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.

author: IanB
author_rank: Super Contributor
timestamp: April 18, 2021, 05:08:33 pm
content: 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.

author: ricko_uk
author_rank: Super Contributor
timestamp: April 18, 2021, 06:49:17 pm
content: Thank you all for the detailed infos and explanations!

author: lucazader
author_rank: Regular Contributor
timestamp: April 18, 2021, 08:12:21 pm
content: 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/s9KePWq3bIn 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.

author: ricko_uk
author_rank: Super Contributor
timestamp: April 18, 2021, 10:34:57 pm
content: Thank you lucazader,that link is handy!

author: T3sl4co1l
author_rank: Super Contributor
timestamp: April 19, 2021, 12:36:50 am
content: 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

author: IanB
author_rank: Super Contributor
timestamp: April 19, 2021, 12:46:25 am
content: Quote from: T3sl4co1l on April 19, 2021, 12:36:50 amWell, 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.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: April 19, 2021, 01:16:19 am
content: Yeah, like I said, in... wait, as I should've said: in C.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

author: Tagli
author_rank: Contributor
timestamp: April 19, 2021, 06:42:56 am
content: When usingC++, you don't need to implement classes for this. You don't even need the newenum class.Classical enums will do.Using classical enums:Code:enum Number {Zero = 0, One = 1, Two = 2};Number n0 = Zero; // OK, as expectedNumber n1 = 1; // Compile errorNumber 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:enum class Number {Zero = 0, One = 1, Two = 2};Number n0 = Zero; // Compile errorNumber n1 = Number::One; // OK, as expectedNumber n2 = static_cast<Number>(2); // Explicit casting, works OK.Number n3 = static_cast<Number>(3); // This one works too.int i1 = n1; // Compile errorint i2 = static_cast<int>(n2); // Works OK.

author: ejeffrey
author_rank: Super Contributor
timestamp: April 19, 2021, 11:46:12 pm
content: 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.

author: szszoke
author_rank: Regular Contributor
timestamp: April 20, 2021, 08:12:00 am
content: 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 Barconst a: MyType = "foo"; // okconst b: MyType = "bar"; // okconst c: MyType = "test"; // compilation errorconst d: MyType = "123; // compilation errorconst e: MyType = null; // compilation errorconst f: MyType = undefined; // compilation errorconst myStr: string = a; // okI 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.