haskell / c2hs

c2hs is a pre-processor for Haskell FFI bindings to C libraries
http://hackage.haskell.org/package/c2hs
Other
199 stars 50 forks source link

Generate an enum that supports Bits instances #235

Open teto opened 5 years ago

teto commented 5 years ago

First of all let me apologize if this is not the correct place to post (as my issue is a priori not with the c2hs software). I have a TcpState data structure generated by c2hs via {#enum TCP_ESTABLISHED as TcpState {underscoreToCase} deriving (Eq, Show)#}. which generates (from the linux headers)

data TcpState = TcpEstablished
              | TcpSynSent
              | TcpSynRecv
              | TcpFinWait1
              | TcpFinWait2
              | TcpTimeWait
              | TcpClose
              | TcpCloseWait
              | TcpLastAck
              | TcpListen
              | TcpClosing
              | TcpNewSynRecv
              | TcpMaxStates
  deriving (Eq,Show)
instance Enum TcpState where
  succ TcpEstablished = TcpSynSent
  succ TcpSynSent = TcpSynRecv
  succ TcpSynRecv = TcpFinWait1
  succ TcpFinWait1 = TcpFinWait2
  succ TcpFinWait2 = TcpTimeWait
  succ TcpTimeWait = TcpClose
  succ TcpClose = TcpCloseWait
  succ TcpCloseWait = TcpLastAck
  succ TcpLastAck = TcpListen
  succ TcpListen = TcpClosing
  succ TcpClosing = TcpNewSynRecv
  succ TcpNewSynRecv = TcpMaxStates
  succ TcpMaxStates = error "TcpState.succ: TcpMaxStates has no successor"

  pred TcpSynSent = TcpEstablished
  pred TcpSynRecv = TcpSynSent
  pred TcpFinWait1 = TcpSynRecv
  pred TcpFinWait2 = TcpFinWait1
  pred TcpTimeWait = TcpFinWait2
  pred TcpClose = TcpTimeWait
  pred TcpCloseWait = TcpClose
  pred TcpLastAck = TcpCloseWait
  pred TcpListen = TcpLastAck
  pred TcpClosing = TcpListen
  pred TcpNewSynRecv = TcpClosing
  pred TcpMaxStates = TcpNewSynRecv
  pred TcpEstablished = error "TcpState.pred: TcpEstablished has no predecessor"

  enumFromTo from to = go from
    where
      end = fromEnum to
      go v = case compare (fromEnum v) end of
                 LT -> v : go (succ v)
                 EQ -> [v]
                 GT -> []

  enumFrom from = enumFromTo from TcpMaxStates

  fromEnum TcpEstablished = 1
  fromEnum TcpSynSent = 2
  fromEnum TcpSynRecv = 3
  fromEnum TcpFinWait1 = 4
  fromEnum TcpFinWait2 = 5
  fromEnum TcpTimeWait = 6
  fromEnum TcpClose = 7
  fromEnum TcpCloseWait = 8
  fromEnum TcpLastAck = 9
  fromEnum TcpListen = 10
  fromEnum TcpClosing = 11
  fromEnum TcpNewSynRecv = 12
  fromEnum TcpMaxStates = 13

  toEnum 1 = TcpEstablished
  toEnum 2 = TcpSynSent
  toEnum 3 = TcpSynRecv
  toEnum 4 = TcpFinWait1
  toEnum 5 = TcpFinWait2
  toEnum 6 = TcpTimeWait
  toEnum 7 = TcpClose
  toEnum 8 = TcpCloseWait
  toEnum 9 = TcpLastAck
  toEnum 10 = TcpListen
  toEnum 11 = TcpClosing
  toEnum 12 = TcpNewSynRecv
  toEnum 13 = TcpMaxStates
  toEnum unmatched = error ("TcpState.toEnum: Cannot match " ++ show unmatched)

I would like to add Data.Bits instance to TcpState so that I can write TcpEstablished .|. TcpListen. I've tried to derive Bits in {#enum TCP_ESTABLISHED as TcpState {underscoreToCase} deriving (Eq, Show, Bits)#} but it generates:

Generated.chs:43:21: error:
    • Can't make a derived instance of ‘Bits TcpState’:
        ‘Bits’ is not a stock derivable class (Eq, Show, etc.)
        Try enabling DeriveAnyClass

I believe I could hack something together but as it seems like a common usecase. I wondered if there weren't some trick I could use straight from c2hs (could not find it on the wiki).