D straight array indexed by an enum - arrays

In my D program, I have a read-only array of fixed length and I wish to index the array by an enumerated type.
If I do something like
static const my_struct_t aray[ my_enum_t ] = ... whatever ...;
my_enum_t index;
result = aray[ index ];
then the code produced by GDC is huge, full of calls to the runtime when the array is indexed. So it looks as if either the array is being treated as variable-length or as an associative array (hash table) or something, anyway far from a lightweight C-style array of fixed length with straightforward indexing. Since enums have a fixed cardinality and can't grow, and I have a modest sparse range of values (I'm not misusing the keyword enum just to defined a load of random constants) then I don't know why this happened.
I fixed the issue by changing the line to
static const my_struct_t aray[ my_enum_t.max + 1 ]
and as I understand it that will mean the value in the square brackets is just a known constant of integral type. Since the index is now not an enum at all, I now have an array indexed by an integer, so I have lost type checking, I could index it with any random integer typed variable rather than ensuring that only the correct (strong) type is used.
What should I be doing?
In the more general case, (silly example)
static const value_t aray[ bool ] = blah
for example, where I have an index type that is perfectly sensible semantically, but not just a typeless size_t/int/uint I presume I would get the same problem.
I wouldn't want to say that this is a compiler design problem. It's certainly a case of sub-optimal behaviour. But to be fair to the compiler what exactly is telling it whether the array is fixed-length or variable, and sparse or dense? I want two things; type checking of the index and non-variable length. Actually, in this particular case the array is const (I could have put immutable just as well) so it clearly can't be variable-length any way. But with an array that has modifiable content but is of fixed length you need to be able to declare that it is fixed-length.

V[K] name is the syntax for an associative array which does indeed do runtime calls and such, even when the type is limited to a small number of values like bool or an enum. The compiler probably could optimize that, making it act to the program like an AA while implementing it as a simple fixed-length array, but it doesn't; it treats all key types the same.
I would suggest going with what you started: T[enum.max + 1], but then doing a wrapper if you want to force type safety. You can make the index overloads static if you only want one instance of it:
enum Foo {
one,
two
}
struct struct_t {}
struct array {
static private struct_t[Foo.max + 1] content;
static struct_t opIndex(Foo idx) { return content[cast(int) idx]; }
}
void main() {
struct_t a = array[Foo.one];
}
Then, you can just genericize that if you want simpler reuse.
struct enum_array(Key, Value) {
static private struct_t[Key.max + 1] content;
static Value opIndex(Key idx) { return content[cast(int) idx]; }
}
alias array = enum_array!(Foo, struct_t);
Or, of course, you don't need to make it static, you could do a regular instance too, and initialize the contents inside and such.

In D, both static and dynamic arrays are indexed by size_t, just like they would be in C and C++. And you can't change the type of the index in D any more than you can in C or C++. So, in D, if you put a type between the brackets in the array declaration, you're defining an associative array and not a static array. If you want a static array, you must provide an integer literal or compile-time constant, and there is no way to require that a naked, static array be indexed by an enum type that has a base type of size_t or a type that implicitly converts to size_t.
If you want to require that your static array be indexed by a type other than size_t, then you need to wrap it in a struct or class and control the access to the static array via the member functions. You could overload opIndex to take your enum type and treat your struct type as if it were a static array. So, the effect should then be effectively what you were trying to do with putting the enum type in the static array declaration, but it would be the member function that took the enum value and called the static array with it rather than doing anything to the static array itself.

Related

Is there a way to assign a unique number to a type in C?

I am coming from a C++ background, and have recently taken up C. I am am having trouble assigning a number to a type (or vice versa); what I need is some way to assign a unique ID to a type, preferably starting from 0. My goal is to have a function (or macro) that indexes an array based on a passed-in type, which I believe to only be achievable through macros.
Also, since I use the sizeof() the type which I need to be passed in, it makes using enums as an alternative difficult. If I were to pass the enumerator to the function/macro instead, then I would have to get the type from the number, the exact opposite (but maybe easier) problem.
Is this even possible in C? I have tried researching this question, but have not found any answer to this problem particularly, which I was able to do in C++ with templates, like so:
int curTypeIdx = 0;
template <typename T>
struct TypeHandle {
static int const val;
}
template <typename T>
int const TypeHandle<T>::val = curTypeIdx++;
The reason for this is that I am building an ECS. I have an EntityManager struct, which is supposed to contain arrays of components. Since I plan for this to be general-purpose, I defined an upper limit of components (MAX_COMPONENTS) and have an array of char*s of length MAX_COMPONENTS. At a basic level, The goal is to give the user of the EntityManager the ability to define their own components, and store them in these generic arrays.
If there is any other way to
Thank you all for any advice.
If you are OK with enumerating ALL supported types once( and update the list if language comes up with new types), then you can use the stringize functionality of C macros and an array of strings to achieve what you want.
#define GET_TYPE_ID(type) get_type_id(#type)
const char *type_strings[] = { "char", "unsigned char", "short" /* so on.. */};
int get_type_id(const char* type_string) {
for( int i = 0; i < sizeof(type_strings)/sizeof(const char*); i++) {
if ( strcmp(type_string, type_strings[i]) == 0 ) return i;
}
// Should never reach here if you have taken care of all types and
// don't pass in illegal types.
}
Now you can get an integer ID for each type with GET_TYPE_ID(int), GET_TYPE_ID(char) and so on.

C: Why can you pass (to a function) a struct by value, but not an array?

Any historical or logical reasons behind it?
Explanation:
when you pass an array to a function in C you actually only pass a pointer to an array.
However, when you pass a struct, you can either pass a copy of the struct or the pointer.
//this:
int function(int array[10])
// is equivalent to this:
int function(int *array)
//and they both pass a pointer
//but this:
int function(struct tag name)
//passes a struct by value, where as this:
int function(struct tag *name)
//passes a pointer to it.
Why the difference?
In the original K&R, you could not pass structs by value. That was a syntax error. Because many compiler vendors offered it as an extension, pass-by-value eventually made its way into the standard.
Why the restriction, and why the evolution? The machines that C was developed on were tiny. Segment sizes of 64 kilobytes were common. Memory was precious, so why copy something when you could just pass the address? Duplicating a 64-byte structure on the stack was an error, probably not even what the user intended.
By the mid-1990s, that wasn't true anymore. 32-bit addressing and RAM of 4 MB or more were common. The restriction was a hinderance and led to some complexity, because without const a structure passed by reference could be modified, perhaps unwittingly.
Why not do the same with arrays? No demand. Arrays and pointers are closely related in C, as you know. The C standard library depends heavily on passing by reference, consider memset and strcpy. Whereas passing a struct by value meant just dropping the & on the call, passing an array by value would have entailed adding new syntax. A compiler vendor that offered, say, by value as C syntax would have been laughed out of the conference.
int function(int array[10]) and int function(int *array) are the same because of 6.7.5.3 Function declarators (including prototypes) (http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf page 118)
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted
to ‘‘qualified pointer to type’’, where the type qualifiers (if
any) are those specified within the [ and ] of the array type
derivation. If the keyword static also appears within the [ and
] of the array type derivation, then for each call to the
function, the value of the corresponding actual argument shall
provide access to the first element of an array with at least as many
elements as specified by the size expression
.
Of course, you can pass an array by value; all you need to do is wrap it in a struct. But that only works if the array has a definite (and non-variable) size. You can include an indefinitely sized array in a struct, but the resulting type is incomplete and can only be used as the target of a pointer.
That's probably as close as we're going to get to an explanation. The vast majority of arrays passed as arguments are not of fixed size, and passing them by value would not be possible, even if it were intended, which is also unlikely.
There is a similar (but different) decay with functions; a function cannot be passed as an argument, only function pointers. Since it would be tedious to explicitly write & every time you wanted to refer to a function, the language takes care of it for you.
On the whole, questions of the form "Why is this language like this snd not like that?" can only be answered "because that's how it is".
Types are different in both function declarations -
struct tag /* and */ struct tag *
One is structure variable whereas another is pointer to structure.
You can do similar with structures -
int function(struct tag name[]) /*--> int function(struct tag *name) */
These above would be equivalent.
structs are used for declaring own kind of data types with primitive data types like int,float,long(or structs of structs) etc. they are supposed to hold a few of them e.g. struct of students will contain id,name,rollno,subjects,etc. so mostly any struct element will contain 10-20 fields at most (in logical cases), so when you pass a struct to a function, it has to copy 40-100 bytes approx. to make copy of that struct variables. where as arrays can be of huge size and are used to store same kind of information. they can be of size 10^7 in case of integers so if we implement a language to copy whole array for function calls it may have to copy (10^7)*4 bytes which is a huge amount and will impact the performance badly.and typical size of arrays is 10^4 to 10^6 which is still a lot. but if you create struct of array of int(or any other array) you can pass it to a function as a copy of that array. e.g.
#include<stdio.h>
typedef struct {
int arr[10];
}arrayStruct;
void change(arrayStruct a){
a.arr[2]=5;
}
int main(){
arrayStruct a;
for(int i=0;i<10;i++){
a.arr[i]=i;
}
printf("Before:\n");
for(int i=0;i<10;i++){
printf("%d ",a.arr[i]);
}
change(a);
printf("\nAfter:\n");
for(int i=0;i<10;i++){
printf("%d ",a.arr[i]);
}
return 0;
}
this is not done in most of the cases but few times when you need to pass arrays but don't want to alter the contents of them but also require some kind of changes to their copies and want to return that copy you can use this kind of structs of arrays and return them from functions of return type of structs e.g.
arrayStruct returnChange(arrayStruct a){
a.arr[2]=332;
return a;
}

Array definition - Expression must have a constant value

I am creating an array on stack as
static const int size = 10;
void foo() {
..
int array[size];
..
}
However, I get the compile error: "expression must have a constant value", even though size is a constant. I can use the macro
#define SIZE (10)
But I am wondering why size marked const causes compilation error.
In C language keyword const has nothing to do with constants. In C language, by definition the term "constant" refers to literal values and enum constants. This is what you have to use if you really need a constant: either use a literal value (define a macro to give your constant a name), or use a enum constant.
(Read here for more details: Shall I prefer constants over defines?)
Also, in C99 and later versions of the language it possible to use non-constant values as array sizes for local arrays. That means that your code should compile in modern C even though your size is not a constant. But you are apparently using an older compiler, so in your case
#define SIZE 10
is the right way to go.
The answer is in another stackoverflow question, HERE
it's because In C objects declared with the const modifier aren't true
constants. A better name for const would probably be readonly - what
it really means is that the compiler won't let you change it. And you
need true constants to initialize objects with static storage (I
suspect regs_to_read is global).
if you are on C99 your IDE compiler option may have a thing called variable-length array (VLA) enable it and you won't get compile error, efficiently without stressing your code though is with MALLOC or CALLOC.
static const int size = 10;
void foo() {
int* array;
array = (int *)malloc(size * sizeof(int));
}

Casting double* to double(*)[N]

void compute(int rows, int columns, double *data) {
double (*data2D)[columns] = (double (*)[columns]) data;
// do something with data2D
}
int main(void) {
double data[25] = {0};
compute(5, 5, data);
}
Sometimes, it'd be very convenient to treat a parameter as a multi-dimensional array, but it needs to be declared as a pointer into a flat array. Is it safe to cast the pointer to treat it as a multidimensional array, as compute does in the above example? I'm pretty sure the memory layout is guaranteed to work correctly, but I don't know if the standard allows pointers to be cast this way.
Does this break any strict aliasing rules? What about the rules for pointer arithmetic; since the data "isn't actually" a double[5][5], are we allowed to perform pointer arithmetic and indexing on data2D, or does it violate the requirement that pointer arithmetic not stray past the bounds of an appropriate array? Is data2D even guaranteed to point to the right place, or is it just guaranteed that we can cast it back and recover data? Standard quotes would be much appreciated.
I apologize in advance for a somewhat vague answer, as someone said these rules in the standard are quite hard to interpret.
C11 6.3.2.3 says
A pointer to an object type may be converted to a pointer to a
different object type. If the resulting pointer is not correctly
aligned for the referenced type, the behavior is undefined.
So the actual cast is fine, as long as both pointers have the same alignment.
And then regarding accessing the actual data through the pointer, C11 6.5 gives you a wall of gibberish text regarding "aliasing", which is quite hard to understand. I'll try to cite what I believe are the only relevant parts for this specific case:
"The effective type of an object for an access to its stored value is
the declared type of the object, if any." /--/
"An object shall have its stored value accessed only by an lvalue
expression that has one of the following types:
a type compatible
with the effective type of the object, "
/--/
"an aggregate or union type that includes one of the aforementioned types among its
members"
(The above is sometimes referred to as the "strict aliasing rule", which isn't a formal C language term, but rather a term made up by compiler implementers.)
In this case, the effective type of the object is an array of 25 doubles. You are attempting to cast it to an array pointer to an array of 5 doubles. Whether it counts as a type compatible with the effective type, or as an aggregate which includes the type, I'm not sure. But I'm quite sure it counts as either of those two valid cases.
So as far as I can see, this code doesn't violate 6.3.2.3 nor 6.5. I believe that the code is guaranteed to work fine and the behavior should be well-defined.
The safest way in such situations is to keep the elements in a flat 1-dimensional array, but write accessor methods to read and write from this array in a multi dimensional way.
#include <stdio.h>
#include <string.h>
const int rowCount = 10;
const int columnCount = 10;
const int dataSize = rowCount*columnCount;
double data[dataSize];
void setValue( const int x, const int y, double value)
{
if ( x>=0 && x<columnCount && y>=0 && y<rowCount) {
data[x+y*columnCount] = value;
}
}
double getValue( const int x, const int y )
{
if ( x>=0 && x<columnCount && y>=0 && y<rowCount) {
return data[x+y*columnCount];
} else {
return 0.0;
}
}
int main()
{
memset(data, 0, sizeof(double)*dataSize);
// set a value
setValue(5, 2, 12.0);
// get a value
double value = getValue(2, 7);
return 0;
}
The example uses global variables which are only used for simplicity. You can either pass the data array as additional parameter to the functions, or even create a context to work with.
In c++ you would wrap the data container into a class and use the two methods as access methods.

Declare a constant array

I have tried:
const ascii = "abcdefghijklmnopqrstuvwxyz"
const letter_goodness []float32 = { .0817,.0149,.0278,.0425,.1270,.0223,.0202, .0609,.0697,.0015,.0077,.0402,.0241,.0675, .0751,.0193,.0009,.0599,.0633,.0906,.0276, .0098,.0236,.0015,.0197,.0007 }
const letter_goodness = { .0817,.0149,.0278,.0425,.1270,.0223,.0202, .0609,.0697,.0015,.0077,.0402,.0241,.0675, .0751,.0193,.0009,.0599,.0633,.0906,.0276, .0098,.0236,.0015,.0197,.0007 }
const letter_goodness = []float32 { .0817,.0149,.0278,.0425,.1270,.0223,.0202, .0609,.0697,.0015,.0077,.0402,.0241,.0675, .0751,.0193,.0009,.0599,.0633,.0906,.0276, .0098,.0236,.0015,.0197,.0007 }
The first declaration and initialization works fine, but the second, third and fourth don't work.
How can I declare and initialize a const array of floats?
An array isn't immutable by nature; you can't make it constant.
The nearest you can get is:
var letter_goodness = [...]float32 {.0817, .0149, .0278, .0425, .1270, .0223, .0202, .0609, .0697, .0015, .0077, .0402, .0241, .0675, .0751, .0193, .0009, .0599, .0633, .0906, .0276, .0098, .0236, .0015, .0197, .0007 }
Note the [...] instead of []: it ensures you get a (fixed size) array instead of a slice. So the values aren't fixed but the size is.
As pointed out by #jimt, the [...]T syntax is sugar for [123]T. It creates a fixed size array, but lets the compiler figure out how many elements are in it.
From Effective Go:
Constants in Go are just that—constant. They are created at compile time, even when defined as locals in functions, and can only be numbers, characters (runes), strings or booleans. Because of the compile-time restriction, the expressions that define them must be constant expressions, evaluatable by the compiler. For instance, 1<<3 is a constant expression, while math.Sin(math.Pi/4) is not because the function call to math.Sin needs to happen at run time.
Slices and arrays are always evaluated during runtime:
var TestSlice = []float32 {.03, .02}
var TestArray = [2]float32 {.03, .02}
var TestArray2 = [...]float32 {.03, .02}
[...] tells the compiler to figure out the length of the array itself. Slices wrap arrays and are easier to work with in most cases. Instead of using constants, just make the variables unaccessible to other packages by using a lower case first letter:
var ThisIsPublic = [2]float32 {.03, .02}
var thisIsPrivate = [2]float32 {.03, .02}
thisIsPrivate is available only in the package it is defined. If you need read access from outside, you can write a simple getter function (see Getters in golang).
There is no such thing as array constant in Go.
Quoting from the Go Language Specification: Constants:
There are boolean constants, rune constants, integer constants, floating-point constants, complex constants, and string constants. Rune, integer, floating-point, and complex constants are collectively called numeric constants.
A Constant expression (which is used to initialize a constant) may contain only constant operands and are evaluated at compile time.
The specification lists the different types of constants. Note that you can create and initialize constants with constant expressions of types having one of the allowed types as the underlying type. For example this is valid:
func main() {
type Myint int
const i1 Myint = 1
const i2 = Myint(2)
fmt.Printf("%T %v\n", i1, i1)
fmt.Printf("%T %v\n", i2, i2)
}
Output (try it on the Go Playground):
main.Myint 1
main.Myint 2
If you need an array, it can only be a variable, but not a constant.
I recommend this great blog article about constants: Constants
As others have mentioned, there is no official Go construct for this. The closest I can imagine would be a function that returns a slice. In this way, you can guarantee that no one will manipulate the elements of the original slice (as it is "hard-coded" into the array).
I have shortened your slice to make it...shorter...:
func GetLetterGoodness() []float32 {
return []float32 { .0817,.0149,.0278,.0425,.1270,.0223 }
}
In addition to #Paul's answer above, you can also do the following if you only need access to individual elements of the array (i.e. if you don't need to iterate on the array, get its length, or create slices out of it).
Instead of
var myArray [...]string{ /* ... */ }
you can do
func myConstArray(n int) string {
return [...]string{ /* ... */ }[n]
}
and then instead of extracting elements as
str := myArray[i]
you extract them as
str := myConstArray(i)
Link on Godbolt: https://godbolt.org/z/8hz7E45eW (note how in the assembly of main no copy of the array is done, and how the compiler is able to even extract the corresponding element if n is known at compile time - something that is not possible with normal non-const arrays).
If instead, you need to iterate on the array or create slices out of it, #Paul's answer is still the way to go¹ (even though it will likely have a significant runtime impact, as a copy of the array needs to be created every time the function is called).
This is unfortunately the closest thing to const arrays we can get until https://github.com/golang/go/issues/6386 is solved.
¹ Technically speaking you can also do it with the const array as described in my answer, but it's quite ugly and definitely not very efficient at runtime: https://go.dev/play/p/rQEWQhufGyK

Resources