I'm trying to create a type to represent a microcontroller register where the address of the register is part of the type. This is the closest I can get to what I want that will compile:
type
RegisterType* = uint8 | uint16 | uint32
RegisterPtr* = ptr RegisterType
RegisterAttr = enum
Readable,
Writable
RegisterAttrs = set[RegisterAttr]
RegisterConfig[
TAddress: static RegisterPtr,
TReadWriteAttr: static RegisterAttrs,
] = distinct NimbedConfig
proc initRegister*(address: static RegisterPtr, rwAttrs: static RegisterAttrs): RegisterConfig[address, rwAttrs] =
discard
and I declare and use a fictional register like this:
const PORTA = initRegister(cast[ptr uint8](0x0100), {Readable, Writable})
let a = PORTA.load()
PORTA.store(a+1)
Can someone suggest how I should implement initRegister() so that the declaration of PORTA becomes a bit cleaner:
const PORTA = initRegister(uint8, 0x0100, {Readable, Writable})
side quest: What is it called when I put a static value such as 0x0100 into the datatype? Is RegisterConfig a "type class"? I need to learn this topic better and am looking for some search terms to use.
type
RegisterType* = uint8 | uint16 | uint32
RegisterPtr* = ptr RegisterType
RegisterAttr = enum
Readable,
Writable
RegisterAttrs = set[RegisterAttr]
NimbedConfig = object
RegisterConfig[
TAddress: static RegisterPtr,
TReadWriteAttr: static RegisterAttrs,
] = distinct NimbedConfig
proc initRegister*[T: RegisterType](address: static int, rwAttrs: static RegisterAttrs): auto = # Generic parameters in proc headers are resolved at declaration generally... need auto.
result = RegisterConfig[cast[ptr T](address), rwAttrs](NimbedConfig()) # Give return type here
const a = initRegister[uint8](0x0100, {Readable, Writable})
Seems to work. RegisterConfig is a typeclass when used as a type with no generic parameters, but if you mean is that the name of a type delimited by statics no. They're just value delimited generics.@ElegantBeef to the rescue, once again, thank you.
My incorrect assumption of a generic procedure was that the type T had to be used in the procedure's signature (i.e. somewhere between proc and the first =); it is good to have that corrected. One of my failed attempts to solve this was to leave out the definition of the return type and let the assignment to result define the type; that, of course, didn't work. auto is the proper thing to do instead. Your tip also helped me realize there is nothing generic about RegisterConfig and I should not prefix the TAddress and TReadWriteAttr field names with T.