Search code examples
cenumsswitch-statement

_Generic Statement For An Enumerator (Enum)?


I frequently have to deal with the functionality of enums in C. However, there doesn't seem to be an easy & equally efficient way of supplying a different result based off of a predefined enum value--at least, not in my situation. Take the below code:

/**
 * @brief An enumerator to represent all the various numerical types that
 * can be stored in the @ref dynamic_array_number_t. This is simply a list
 * of the various integer types that can fit into a signed 64-bit integer.
 */
typedef enum
{
    /**
     * @brief Unsigned 8-bit integer.
     */
    unsigned8,
    /**
     * @brief Unsigned 16-bit integer.
     */
    unsigned16,
    /**
     * @brief Unsigned 32-bit integer.
     */
    unsigned32,
    /**
     * @brief Signed 8-bit integer.
     */
    signed8,
    /**
     * @brief Signed 16-bit integer.
     */
    signed16,
    /**
     * @brief Signed 32-bit integer.
     */
    signed32
} dynamic_array_number_type_t;

/**
 * @brief A dynamic array for integers. Can store an infinite amount of
 * members, if your computer's got the memory. All functions having to do
 * with this type are suffixed with "-N".
 */
typedef struct
{
    /**
     * @brief The list of the array's members. Stored in 8 bytes (64 bits)
     * since it provides integer safety, along with being a sort of generic
     * for all the possible integer types. Do not mess with this value
     * yourself, use helper functions. Do not retrieve values within this
     * array yourself, use helper functions.
     */
    int64_t* members;
    /**
     * @brief The type of integer we're storing in the array. This cannot
     * change, and is therefore marked with const. See @ref
     * dynamic_array_number_type_t for more information.
     */
    const dynamic_array_number_type_t type;
    /**
     * @brief The size of the array (in 64-bit blocks of memory). This is
     * simply a counter of how many numbers it @b can store, not @b is
     * storing. That is stored in the @ref occupied value. Do not increment
     * this value yourself, use helper functions.
     */
    size_t size;
    /**
     * @brief The count of members within the array. Do not mess with this
     * value, the program will very nearly always crash. Use helper
     * functions.
     */
    uint32_t occupied;
} dynamic_array_number_t;

/**
 * @brief Allocate a block of memory of @param size bytes long. This is
 * legitimately just @ref xmalloc but with a name that fits my scheme.
 * @param size The size--in bytes--of the block to allocate.
 * @return A pointer to to newly allocated block.
 */
void* AllocateBlock(size_t size);

/**
 * @brief Create a new dynamic array for integer types. You must free the
 * object returned by this function with @ref DestroyDynamicArrayN.
 * @param type The integer type this array should contain when converted
 * from the default int64_t.
 * @param size The size the array should be off the bat; this can be
 * changed via helper functions like @ref GrowDynamicArrayN or @ref
 * ShrinkDynamicArrayN.
 * @return A brand-spanking-new integer dynamic array.
 */
inline static dynamic_array_number_t
CreateArrayN(dynamic_array_number_type_t type, size_t size)
{
    return (dynamic_array_number_t){AllocateBlock(sizeof(int64_t) * size),
                                    type, size, 0};
}

/**
 * @brief Destroy the given integer array. The array passed into this
 * function, upon the completion of it, shall have none of its prior data
 * beyond the value of its @ref type member, and all deleted data shall
 * have been destroyed irretrievably.
 * @param array The array to destroy.
 */
void DestroyArrayN(dynamic_array_number_t* array);

// This is in a separate C file.
void* AllocateBlock(length_t size)
{
    void* ptr = malloc(size);
    if (ptr == NULL) LogError(failed_allocation);
    return ptr;
}

void DestroyArrayN(dynamic_array_number_t* array)
{
    assert(array != NULL && array->members != NULL);
    free(array->members);
    
    array->members = NULL;
    array->size = 0;
    array->occupied = 0;
}

And an example of array creation:

int main(void) {
    dynamic_array_number_t test_array = CreateArrayN(unsigned8, 3);
    // ... do stuff with array
    DestroyArrayN(&test_array);

    return 0;
}

I need to convert the int64_t within the structure to whatever integer type the user has supplied to the array when it was created, uint8_t, int32_t, whatever. This is easy, of course, using a simple conversion like (int8_t)array.members[0]. However, I want to provide a helper function to make that kind of access easier, however I don't want to just return an int64_t and call it a day; that seems pointless.

Instead, is there a way (similar to a _Generic statement for types) that I can serve a different function for any possible value of a passed-in array's type value?


Solution

  • is there a way (similar to a _Generic statement for types) that I can serve a different function for any possible value of a passed-in array's type value?

    No. The enum value like unsigned8 or unsigned16 is an information available at runtime, but type is something available at compile time. You can't time travel - once you use a value that has a value at runtime it can't affect types at compile time.

    If you want to have type information at compile time, you have to provide that information at compile time. Typically, one would use different types to represent that information at compile time, like struct dynamic_array_signed8 struct dynamic_array_unsigned8 etc, and have several implementations of the same for different types. (You might be interested in STC library https://github.com/stclib/STC or other type-generic containers for C.)

    Overall, using an enum values to represent different types is a bad idea, which results in such super long switches: https://github.com/Gurux/GuruxDLMS.c/blob/master/development/src/gxobjects.c#L1323 . You might be rather interested in virtual methods https://en.wikipedia.org/wiki/Virtual_method_table to implement object specific dynamic dispatch at runtime.