Best way to handle various integer types generically

I got the first version of the firmware for my film scanner’s hardware controller from the developer. It uses Modbus for communication, and he stores data in registers on the controller in a variety of types, that I need access. Some are signed, some unsigned, some 16bit, some 32bit.

I need to be able to read all registers and read/write some. I’ve built a Class that handles the modbus connection via libmodbus and this all seems to be working as expected. But when I did my initial testing I only tested Uint16 registers. I now need to support more. Ideally I’d like my Class.write_register method to be able to handle all types, then call the external method (or a variation of it) with the correct type for the data. I’ve come up with two ways to do this, neither of which I’m really happy with.

One way would be with overloaded methods - both the internal and external methods in my class would need to be overloaded - one for each integer type. The thing i don’t like about this is that there’s a bunch of duplicated code, since I need to check that the parameters passed are valid, log the results or any errors, etc. Here’s the current uint16-only version:

var result as integer = modbus_write_register(mHandle,address,value)

If result > 0 then //success
  LogMessage("Wrote " + value.ToString + " to register " + address.ToString)
else //error
  var error as integer = GetLastErrno()
  var errorstring as string = Get_Modbus_Error(error)
  LogMessage("ERROR: Unable to write " + value.ToString + " to register " + address.ToString)
  LogMessage("ERROR MESSAGE: (" + error.ToString + ") " + errorstring  ) 
end if

I want to avoid duplicating all that code (I suppose I could move the bulk of this into its own logging function that I call from this method, though).

An alternative would be to have the internal method accept a generic Integer. The external method would be overloaded, one version for each type, and my internal method would format the Integer parameter accordingly, depending on what it’s supposed to be for that register. I’d need to have two versions of this internal method though, correct? one for signed and one for unsigned integers?

Or is there another way to handle this that doesn’t involve copy/pasting code into multiple similar-looking methods?

I don’t think you can overload a method based on integer type.

you can:

image

and for the external methods:

Well I’ll be. You have to be very explicit in the variable types you pass in, but it works. I don’t know how that squares with “generically”, though. I would personally name them differently, since you have to know what type to pass anyway. That could make the code clearer.

Write_Int32_Register
Write_Int16_Register

2 Likes

What Tim said.

Some specifics require this kind of approach, there are even some languages removing overloading to force such kind of design, but… on the other side they usually add Generics, Traits/Protocols, Mixins giving more power to avoid being too verbose/redundant.

WelI suppose in the sense that I can call a single method and pass it whatever type I happen to be working with, and it will handle it accordingly. But I don’t really like this setup. I’m thinking if I just have two versions:

Write_Register(address as integer, value as integer)
Write_Register(address as integer, value as uinteger)

then I can handle figuring out what to send the external methods based on that. In the end, I might not need to handle 16 vs 32 here, since the registers are all 16bit. A 32bit value is split across two registers. So really, I just need to handle signed and unsigned ints.

I need more background about your need, because, if your need is always writing a 16bit value, that can or cannot be signed in a transparent way, having just the signed one should solve both cases. All you need is the truncation at 16 bit of a signed bigger one

Write_Register(address as integer, value as Integer)

Var realValue As UInt16 = value // use this

image

What Rick said.

You’re just writing bits. Signed or Unsigned, they’re the same bits.

Huh. I thought I had to explicitly match what I’m sending to an external library with the types it’s expecting, but I’ll take a look at this and test it with the controller itself today. Thanks.

Another way you could handle this would be to write some methods for the registers as opposed to the types being written. That way you could use a memoryblock to split things exactly the way you need to… like stuffing in an Int32 and pulling out two Int16s if you wanted.

Yeah, I think I managed to get myself completely confused here. I am already doing what you decribe. Working with the firmware developer I now have things more or less sorted in my brain. And I’m see what @Rick_Araujo posted above happening in my app, so I think I’ve got a handle on how this is working now.

Here’s my test app.

I’ve made a connection to one of the controllers and wrote a value of -123 to it.

The line that says “Wrote -123 to register 17” is converting the register value to an int16 so it reports a negative number. The next line is the result of reading the register from the controller, without converting it to an int16

So I guess this means I need to maintain a table of the signed registers, so I can convert the register values as soon as I read them in from the controller. If I understand the way this is working, I can send the controller a positive or negative number, but I just need to convert to the proper type when reading the register value back into my app so i know what’s going on.

Considering 65413 = 65536 - 123 I think you have your answer. It looks like you’re reading as unsigned and writing as signed.

Good. :+1:t2:

1 Like

Yes in this case I am. The thing is there are a total of about 40 registers across two controllers. Most are unsigned. But a handful aren’t, so I guess I just need to have a list of the special cases that my code refers to when reading so I’m properly handling those. I think in all but one case the signed values are in read-only registers. so this is mostly about how I handle what the controller is giving me.

You’re making me feel nostalgic for the days of 8 bit processors with 3 registers all of which were integer. Hand coding in machine code (opcodes, rather than Assembly language) and doing things like toggling a memory address to make notes come out of a speaker. Oh, the fun before IBM released a PC and Windows was yet to copy Macintosh, which didn’t exist either. Can anyone say 6502? :grin:

Z80

1 Like

A cheap toy by comparison :smiley:

Yep, that’s why Apple II had a card with Z80 to run CP/M. The only way to run Turbo Pascal on top of that 6502 toy.