Why should I use user defined data types?

Thread Starter

AlbertHall

Joined Jun 4, 2014
12,346
I am writing a program which uses some pre-existing code and I find myself converting from a UDT to individual variables and back again. This is VB6 code and it has some problems with just where and how you can define and use a UDT.

I found on www the following:
The only real advantage to using user-defined data types is that they add the ability to self-document your data structures.

If this is true then I may 'deconstruct' the UDTs.

What do you reckon?
 

djsfantasi

Joined Apr 11, 2010
9,163
The only advantage I’ve found with UDTs is that certain operations are simpler with them. Copying for example. Otherwise, I don’t use them. This may be attributed to ancient history as much as anything else.
 

nsaspook

Joined Aug 27, 2009
13,281
It depends on the language used. In C user-defined data types using typedef, structures and unions can create a powerful programming abstraction of hardware interfacing down to the bit level.

C:
    typedef struct block10_type {
        uint32_t systemb;
        uint8_t bidl;
        uint8_t bidh : 7;
        uint8_t ebit : 1;
        uint8_t function;
        uint8_t stream : 7;
        uint8_t wbit : 1;
        uint8_t didl;
        uint8_t didh : 7;
        uint8_t rbit : 1;
    } block10_type;

    typedef union block10 {
        uint8_t b[sizeof(block10_type)];
        block10_type block;
    } block10;

    typedef struct header10 {
        uint16_t checksum;
        block10 block;
        uint8_t length;
    } header10;

header10 H10[] = {
    { // S1F1 send 'are you there?' from host to equipment
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 1,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // all stream and function header receive buffer from equipment
        .length = 10,
    },
    { // S1F0 send 'ABORT' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 1,
        .block.block.function = 0,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S1F1 send 'are you there?' from equipment to host
        .length = 10,
        .block.block.rbit = 1,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 1,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S2F17 send 'date and time request?' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 2,
        .block.block.function = 17,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S1F15 send 'request off-line ' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 15,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S1F17 send 'request on-line ' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 17,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
};
 

Thread Starter

AlbertHall

Joined Jun 4, 2014
12,346
It depends on the language used. In C user-defined data types using typedef, structures and unions can create a powerful programming abstraction of hardware interfacing down to the bit level.
Yes, I agree. But my present case is mostly 'Point' - two floats, 'Line' - two points, and 'Triangle' - three points. Just to confuse further some are 2D points and some are 3D points.
 

Thread Starter

AlbertHall

Joined Jun 4, 2014
12,346
Curious... What’s a 3D point that is defined by two floats?
Yeah, I noticed that after I had posted. The 3D versions use points with three floats. That just means that I also have to convert the 2D version to 3D and back again. If I had just used X, Y, and (optionally) Z I could just have passed the ones needed - no conversion needed.
 

nsaspook

Joined Aug 27, 2009
13,281
Yes, I agree. But my present case is mostly 'Point' - two floats, 'Line' - two points, and 'Triangle' - three points. Just to confuse further some are 2D points and some are 3D points.
The ability to abstract/hide away even simple type implementation details of objects to a single point definition is important for later code extensions or hardware platform modifications. It's not necessary to write code but it sure helps to understand it 10 year later if you want to add additional capabilities to each UDT object while keeping the older source code compatible with the modifications.
 

Thread Starter

AlbertHall

Joined Jun 4, 2014
12,346
I guess this is going to depend on the language and compiler but is there likely to be a performance advantage or disadvantage to using UDTs as I described above?
 

nsaspook

Joined Aug 27, 2009
13,281
I guess this is going to depend on the language and compiler but is there likely to be a performance advantage or disadvantage to using UDTs as I described above?
For a efficient compiled language there should be little or no hardware run performance change either way with good optimization. The real performance advantage is working with just the abstract implementation details vs also dealing with the specific definition details at every step.
 

BobTPH

Joined Jun 5, 2013
8,969
Here is a function declaration (C because I don't know the basic syntax) for taking two lines and determining their intersection, using individual variables:

void intersect(float x1, float y1, float z1, float x2, float y2, float z2, float *ix, float *iy, float *iz)

Now with a point data structure:

typedef struct point {float x, y, z} point;

point intersect(point p1, point p2);

Do you see the improvement?

Bob
 

Thread Starter

AlbertHall

Joined Jun 4, 2014
12,346
Do you see the improvement?
Now suppose that the calling routine has calculated the individual values. You then need to add this before the call:
P1.x = X1;
P1.y = Y1;
P2.x = X2;
P2.y = Y2;

then the call:
P = intersect(P1, P2);

Then after the call you need to extract the return values:
X = P.X;
Y = P.Y;

Now it doesn't look like much of an improvement.
 

xox

Joined Sep 8, 2017
838
Here is a function declaration (C because I don't know the basic syntax) for taking two lines and determining their intersection, using individual variables:

void intersect(float x1, float y1, float z1, float x2, float y2, float z2, float *ix, float *iy, float *iz)

Now with a point data structure:

typedef struct point {float x, y, z} point;

point intersect(point p1, point p2);

Do you see the improvement?

Bob
Too many temporaries. Just pass pointers to your structures so you can avoid making all those unnecessary copies.
 

nsaspook

Joined Aug 27, 2009
13,281
Now suppose that the calling routine has calculated the individual values. You then need to add this before the call:
P1.x = X1;
P1.y = Y1;
P2.x = X2;
P2.y = Y2;

then the call:
P = intersect(P1, P2);

Then after the call you need to extract the return values:
X = P.X;
Y = P.Y;

Now it doesn't look like much of an improvement.
Why would you need to extract the return values when they are already stored in P?
 

WBahn

Joined Mar 31, 2012
30,062
I am writing a program which uses some pre-existing code and I find myself converting from a UDT to individual variables and back again. This is VB6 code and it has some problems with just where and how you can define and use a UDT.

I found on www the following:
The only real advantage to using user-defined data types is that they add the ability to self-document your data structures.

If this is true then I may 'deconstruct' the UDTs.

What do you reckon?
There are several advantages, in addition to making your code more readable and maintainable.

Most languages are able to do type checking on user defined data types, so if you have two structures, one for triangles and one for ducks, and you pass a duck variable to a function that is expecting a triangle variable, the compiler can at least warn you of the likely logic error even if the actual data within each structure are identical types.

You can also pick up significant performance gains since you can now pass a single reference value back and forth instead of copying the contents of each internal value every time you pass them to a function.

It also makes it far simpler -- and more efficient -- to dynamically allocate large structures so that you don't have to have all the data on the stack, which is limited in size compared to the heap.
 

MrSoftware

Joined Oct 29, 2013
2,200
There are countless reasons to use UDT, it all depends on your situation. One example of how it helped in my past; we wrote code to build a physical image of multiple file systems in memory, which would later be written out to optical disc. It helped significantly to create data types that matched the file system specifications down to the bit level. It would have been a lot more complicated to try to assemble the file systems in RAM, and be able to read through and alter them, without using data types that matched the spec.

As far as graphics (points, lines, etc..); if you're going to use hardware acceleration, or if your hardware can take data via DMA, then you will need to lay your data out in RAM in the format that the hardware is expecting. Creating a UDT that matches what the hardware is expecting can make your life easier.
 

bogosort

Joined Sep 24, 2011
696
I'll provide a dissenting opinion: generally speaking, user-defined data types should be avoided. There are cases where they make good sense, e.g., in library APIs, where opacity is desirable. But unless there is compelling reason to abstract the data type, one should assume that the reader of your code would prefer to see the actual type at points of use. I usually cringe when I see application code with made-up data types, as it often feels like abstraction for abstraction's sake, and that can make understanding unfamiliar code more difficult than it needs to be.

Not looking for a fight; I know that many talented programmers here like user-defined types. Just offering an opposing opinion for the OP's sake. :)
 

WBahn

Joined Mar 31, 2012
30,062
I'll provide a dissenting opinion: generally speaking, user-defined data types should be avoided. There are cases where they make good sense, e.g., in library APIs, where opacity is desirable. But unless there is compelling reason to abstract the data type, one should assume that the reader of your code would prefer to see the actual type at points of use. I usually cringe when I see application code with made-up data types, as it often feels like abstraction for abstraction's sake, and that can make understanding unfamiliar code more difficult than it needs to be.

Not looking for a fight; I know that many talented programmers here like user-defined types. Just offering an opposing opinion for the OP's sake. :)
I'll offer a dissenting opinion to your dissenting opinion. :D

Readers of code should want to see data represented at a level of abstraction consistent with the level of code they are reading. I certainly don't want to always see what the actual data types are at the point of use within, for instance, the FILE abstract data type.

Beyond that, if you don't use user-defined data types then you risk making your code unmaintainable. For instance, when programming in C and wanting to use the output of the clock() function, you use a variable of type clock_t. That is a user-defined data type. If you use the actual data type instead and then proceed to compile your code on a different compiler, there is a good chance that, sooner or later, your code will either not compile or you will have runtime logic errors because those functions in the other compiler use a different data type. But if you use clock_t, they will always be compatible. The same is true for functions written by the programmer.
 

MrSoftware

Joined Oct 29, 2013
2,200
I agree abstracting for the sake of abstracting is generally not a good idea. But as stated above, multi-platform code is another place where UDT can be extremely handy. For example, one of my jobs was developing a library that ran (same code) on multiple platforms (Windows, Linux, Irix, OSX, AIX, Solaris) and multiple processors (x86, x64, SPARC, PowerPC) , and was compiled on multiple different compilers (Microsoft Visual Studio, gcc, Sun Studio, XCode), and interacted with hardware at the low level (hard disks, optical discs, tape drives). So we had our own types for things such as int32, uint32, int64, FileHandle, etc.. This way you always knew what you were working with, regardless of platform or compiler. We also wrote our own routines for lower level operations, such as opening and writing files. For example: FileHandle my_file_open(char* file_path); Now use my_file_open() everywhere in our code, and when you change platforms the only thing that has to be re-written is the my_file_open() function and the user defined type FileHandle. All the rest of the code works as-is.
 
Top