diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/hoppy-generator.cabal b/hoppy-generator.cabal
--- a/hoppy-generator.cabal
+++ b/hoppy-generator.cabal
@@ -1,12 +1,12 @@
 name: hoppy-generator
-version: 0.8.0
+version: 0.9.0
 synopsis: C++ FFI generator - Code generator
-homepage: http://khumba.net/projects/hoppy
+homepage: https://khumba.net/projects/hoppy
 license: AGPL-3
 license-file: LICENSE
 author: Bryan Gardiner <bog@khumba.net>
 maintainer: Bryan Gardiner <bog@khumba.net>
-copyright: Copyright 2015-2021 Bryan Gardiner
+copyright: Copyright 2015-2024 Bryan Gardiner
 category: Foreign
 build-type: Simple
 cabal-version: 1.24
@@ -53,16 +53,16 @@
       GeneralizedNewtypeDeriving
     , UndecidableInstances
   build-depends:
-      base >=4.7 && <5
-    , bytestring >=0.10 && <0.11
+      base >=4.10 && <5
+    , bytestring >=0.10 && <0.12
     , containers >=0.5 && <0.7
     , directory >=1.2 && <1.4
     , filepath >=1.3 && <1.5
     , haskell-src >=1.0 && <1.1
-    , mtl >=2.2.1 && <2.3
+    , mtl >=2.2.1 && <2.4
     , process >=1.2 && <1.7
     , temporary >=1.2 && <1.4
-    , text >=1.1 && <1.3
+    , text >=1.1 && <2.2
   hs-source-dirs: src
   ghc-options: -W -fwarn-incomplete-patterns -fwarn-unused-do-bind
   default-language: Haskell2010
diff --git a/src/Foreign/Hoppy/Generator/Common.hs b/src/Foreign/Hoppy/Generator/Common.hs
--- a/src/Foreign/Hoppy/Generator/Common.hs
+++ b/src/Foreign/Hoppy/Generator/Common.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Common/Consume.hs b/src/Foreign/Hoppy/Generator/Common/Consume.hs
--- a/src/Foreign/Hoppy/Generator/Common/Consume.hs
+++ b/src/Foreign/Hoppy/Generator/Common/Consume.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -53,11 +53,11 @@
   fmap = liftM
 
 instance Monad m => Applicative (ConsumeT s m) where
-  pure = return
+  pure = ConsumeT . pure
   (<*>) = ap
 
 instance Monad m => Monad (ConsumeT s m) where
-  return = ConsumeT . return
+  return = pure
   m >>= f = ConsumeT $ getConsumeT . f =<< getConsumeT m
 
 instance MonadTrans (ConsumeT s) where
diff --git a/src/Foreign/Hoppy/Generator/Compiler.hs b/src/Foreign/Hoppy/Generator/Compiler.hs
--- a/src/Foreign/Hoppy/Generator/Compiler.hs
+++ b/src/Foreign/Hoppy/Generator/Compiler.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Hook.hs b/src/Foreign/Hoppy/Generator/Hook.hs
--- a/src/Foreign/Hoppy/Generator/Hook.hs
+++ b/src/Foreign/Hoppy/Generator/Hook.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -324,6 +324,9 @@
 internalEvaluateEnumsForInterface ::
      Interface
   -> Maybe FilePath
+     -- ^ A optional directory to add to the compiler's header search path.
+     -- This is meant to be the directory holding C++ files included in the C++
+     -- package.
   -> Bool
   -> IO (M.Map ExtName EvaluatedEnumData)
 internalEvaluateEnumsForInterface iface maybeCppDir keepBuildFailures = do
diff --git a/src/Foreign/Hoppy/Generator/Hook.hs-boot b/src/Foreign/Hoppy/Generator/Hook.hs-boot
--- a/src/Foreign/Hoppy/Generator/Hook.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Hook.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Language/Cpp.hs b/src/Foreign/Hoppy/Generator/Language/Cpp.hs
--- a/src/Foreign/Hoppy/Generator/Language/Cpp.hs
+++ b/src/Foreign/Hoppy/Generator/Language/Cpp.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Language/Cpp.hs-boot b/src/Foreign/Hoppy/Generator/Language/Cpp.hs-boot
--- a/src/Foreign/Hoppy/Generator/Language/Cpp.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Language/Cpp.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -20,14 +20,18 @@
   SayExportMode,
   ) where
 
+import qualified Data.Set as S
 import Control.Monad.Reader (ReaderT)
 import Control.Monad.Writer (WriterT)
-import {-# SOURCE #-} Foreign.Hoppy.Generator.Spec.Base (ErrorMsg)
+import {-# SOURCE #-} Foreign.Hoppy.Generator.Spec.Base (ErrorMsg, Include)
 
 type Generator = ReaderT Env (WriterT [Chunk] (Either ErrorMsg))
 
 data Env
 
-newtype Chunk = Chunk { chunkContents :: String }
+data Chunk = Chunk
+  { chunkContents :: !String
+  , chunkIncludes :: !(S.Set Include)
+  }
 
 data SayExportMode
diff --git a/src/Foreign/Hoppy/Generator/Language/Cpp/Internal.hs b/src/Foreign/Hoppy/Generator/Language/Cpp/Internal.hs
--- a/src/Foreign/Hoppy/Generator/Language/Cpp/Internal.hs
+++ b/src/Foreign/Hoppy/Generator/Language/Cpp/Internal.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Language/Haskell.hs b/src/Foreign/Hoppy/Generator/Language/Haskell.hs
--- a/src/Foreign/Hoppy/Generator/Language/Haskell.hs
+++ b/src/Foreign/Hoppy/Generator/Language/Haskell.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Language/Haskell.hs-boot b/src/Foreign/Hoppy/Generator/Language/Haskell.hs-boot
--- a/src/Foreign/Hoppy/Generator/Language/Haskell.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Language/Haskell.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Language/Haskell/Internal.hs b/src/Foreign/Hoppy/Generator/Language/Haskell/Internal.hs
--- a/src/Foreign/Hoppy/Generator/Language/Haskell/Internal.hs
+++ b/src/Foreign/Hoppy/Generator/Language/Haskell/Internal.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Main.hs b/src/Foreign/Hoppy/Generator/Main.hs
--- a/src/Foreign/Hoppy/Generator/Main.hs
+++ b/src/Foreign/Hoppy/Generator/Main.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -79,8 +79,6 @@
     -- ^ Generates Haskell bindings for an interface in the given location.
   | CleanCpp FilePath
     -- ^ Removes the generated files in C++ bindings.
-  | CleanHs FilePath
-    -- ^ Removes the generated files in Haskell bindings.
   | KeepTempOutputsOnFailure
     -- ^ Instructs the generator to keep on disk any temporary programs or files
     -- created, in case of failure.
@@ -348,7 +346,8 @@
     , "  --list-interfaces           Lists the interfaces compiled into this binary."
     , "  --list-cpp-files            Lists generated file paths in C++ bindings."
     , "  --list-hs-files             Lists generated file paths in Haskell bindings."
-    , "  --gen-cpp <outdir>          Generate C++ bindings in a directory."
+    , "  --gen-cpp <outdir> <cppdir> Generate C++ bindings in outdir, using bundled"
+    , "                              sources in cppdir."
     , "  --gen-hs <outdir>           Generate Haskell bindings under the given"
     , "                              top-level source directory."
     , "  --clean-cpp <outdir>        Removes generated file paths in C++ bindings."
@@ -409,29 +408,30 @@
           mapM_ putStrLn $ M.keys $ Haskell.generatedFiles gen
           (ListHsFiles:) <$> processArgs stateVar rest
 
-    "--gen-cpp":baseDir:rest -> do
-      baseDirExists <- doesDirectoryExist baseDir
-      unless baseDirExists $ do
+    "--gen-cpp":genDir:cppPackagedSourcesDir:rest -> do
+      genDirExists <- doesDirectoryExist genDir
+      unless genDirExists $ do
         hPutStrLn stderr $
           "--gen-cpp: Please create this directory so that I can generate files in it: " ++
-          baseDir
+          genDir
         exitFailure
-      genResult <- withCurrentCache stateVar $ getGeneratedCpp $ Just baseDir
+      genResult <- withCurrentCache stateVar $ getGeneratedCpp $
+        if null cppPackagedSourcesDir then Nothing else Just cppPackagedSourcesDir
       case genResult of
         Left errorMsg -> do
           hPutStrLn stderr $ "--gen-cpp: Failed to generate: " ++ errorMsg
           exitFailure
         Right gen -> do
           forM_ (M.toList $ Cpp.generatedFiles gen) $
-            uncurry $ writeGeneratedFile baseDir
-          (GenCpp baseDir:) <$> processArgs stateVar rest
+            uncurry $ writeGeneratedFile genDir
+          (GenCpp genDir:) <$> processArgs stateVar rest
 
-    "--gen-hs":baseDir:rest -> do
-      baseDirExists <- doesDirectoryExist baseDir
-      unless baseDirExists $ do
+    "--gen-hs":genDir:rest -> do
+      genDirExists <- doesDirectoryExist genDir
+      unless genDirExists $ do
         hPutStrLn stderr $
           "--gen-hs: Please create this directory so that I can generate files in it: " ++
-          baseDir
+          genDir
         exitFailure
       genResult <- withCurrentCache stateVar getGeneratedHaskell
       case genResult of
@@ -440,13 +440,14 @@
           exitFailure
         Right gen -> do
           forM_ (M.toList $ Haskell.generatedFiles gen) $ \(subpath, contents) ->
-            writeGeneratedFile baseDir subpath contents
-          (GenHaskell baseDir:) <$> processArgs stateVar rest
+            writeGeneratedFile genDir subpath contents
+          (GenHaskell genDir:) <$> processArgs stateVar rest
 
-    "--clean-cpp":baseDir:rest -> do
-      baseDirExists <- doesDirectoryExist baseDir
-      when baseDirExists $ do
-        genResult <- withCurrentCache stateVar $ getGeneratedCpp $ Just baseDir
+    "--clean-cpp":genDir:cppPackagedSourcesDir:rest -> do
+      genDirExists <- doesDirectoryExist genDir
+      when genDirExists $ do
+        genResult <- withCurrentCache stateVar $ getGeneratedCpp $
+          if null cppPackagedSourcesDir then Nothing else Just cppPackagedSourcesDir
         case genResult of
           Left errorMsg -> do
             hPutStrLn stderr $ "--clean-cpp: Failed to evaluate interface: " ++ errorMsg
@@ -454,22 +455,8 @@
           Right gen -> do
             -- TODO Remove empty directories.
             forM_ (M.keys $ Cpp.generatedFiles gen) $ \path ->
-              removeFile $ baseDir </> path
-      (CleanCpp baseDir:) <$> processArgs stateVar rest
-
-    "--clean-hs":baseDir:rest -> do
-      baseDirExists <- doesDirectoryExist baseDir
-      when baseDirExists $ do
-        genResult <- withCurrentCache stateVar $ getGeneratedHaskell
-        case genResult of
-          Left errorMsg -> do
-            hPutStrLn stderr $ "--clean-hs: Failed to evaluate interface: " ++ errorMsg
-            exitFailure
-          Right gen -> do
-            -- TODO Remove empty directories.
-            forM_ (M.keys $ Haskell.generatedFiles gen) $ \path ->
-              removeFile $ baseDir </> path
-      (CleanHs baseDir:) <$> processArgs stateVar rest
+              removeFile $ genDir </> path
+      (CleanCpp genDir:) <$> processArgs stateVar rest
 
     "--dump-ext-names":rest -> do
       withCurrentCache stateVar $ \_ iface cache -> do
diff --git a/src/Foreign/Hoppy/Generator/Override.hs b/src/Foreign/Hoppy/Generator/Override.hs
--- a/src/Foreign/Hoppy/Generator/Override.hs
+++ b/src/Foreign/Hoppy/Generator/Override.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec.hs b/src/Foreign/Hoppy/Generator/Spec.hs
--- a/src/Foreign/Hoppy/Generator/Spec.hs
+++ b/src/Foreign/Hoppy/Generator/Spec.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Base.hs b/src/Foreign/Hoppy/Generator/Spec/Base.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Base.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Base.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -1102,7 +1102,7 @@
   getExportExceptionClass :: a -> Maybe Class
   getExportExceptionClass _ = Nothing
 
--- | Specifies some C++ object (function or class) to give access to.
+-- | Specifies some C++ declaration (function, class, etc.) to give access to.
 data Export = forall a. Exportable a => Export a
 
 instance HasAddendum Export where
@@ -1301,6 +1301,9 @@
 -- | The root data type for specifying how conversions happen between C++ and foreign
 -- values.
 --
+-- Prefer 'makeConversionSpec' to using this data constructor directly, then
+-- override the record to specify conversions for foreign languages.
+--
 -- The @Cpp@ component of this data structure specifies a C++ type, and
 -- conversions between it and something that can be marshalled over a C FFI
 -- layer, if such a conversion is possible in each direction.
@@ -1341,7 +1344,8 @@
 -- | For a 'ConversionSpec', defines the C++ type and conversions to and from a
 -- C FFI layer.
 --
--- Prefer 'makeConversionSpecCpp' to using this data constructor directly.
+-- Prefer 'makeConversionSpecCpp' to using this data constructor directly, then
+-- override the record to specify additional conversion properties.
 --
 -- 'conversionSpecCppName' specifies the C++ type of the conversion.  This will
 -- be the type that is passed over the C FFI as well, unless
@@ -1385,7 +1389,8 @@
     -- convert a value passed over the FFI from the C FFI type (see
     -- 'conversionSpecCppConversionType') to the C++ type
     -- (i.e. 'conversionSpecCppName').  When the former is absent, this is
-    -- always fine.
+    -- always fine.  "Implicitly" here means in the sense of a C++ implicit
+    -- conversion, not necessarily binary compatibility.
     --
     -- When present, this provides custom conversion behaviour for receiving a
     -- value passed into C++ over the FFI.  The function should generate C++
@@ -1414,6 +1419,8 @@
     -- /out of/ C++ through the FFI.  This powers the @ConversionSpec@ being
     -- used as 'Foreign.Hoppy.Generator.Spec.Function.Function' return values
     -- and 'Foreign.Hoppy.Generator.Spec.Callback.Callback' arguments.
+    --
+    -- Arguments are the same as with 'conversionSpecCppConversionToCppExpr'.
   }
 
 -- | Builds a 'ConversionSpecCpp' with a C++ type, with no conversions defined.
diff --git a/src/Foreign/Hoppy/Generator/Spec/Base.hs-boot b/src/Foreign/Hoppy/Generator/Spec/Base.hs-boot
--- a/src/Foreign/Hoppy/Generator/Spec/Base.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Spec/Base.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -17,6 +17,7 @@
 
 module Foreign.Hoppy.Generator.Spec.Base (
   ErrorMsg,
+  Include,
   Reqs,
   ExtName,
   Identifier,
@@ -24,9 +25,13 @@
   HsImportSet,
   ) where
 
+import qualified Data.Set as S
+
 type ErrorMsg = String
 
-data Reqs
+newtype Include = Include { includeToString :: String }
+
+newtype Reqs = Reqs { reqsIncludes :: S.Set Include }
 
 newtype ExtName = ExtName { fromExtName :: String }
 
diff --git a/src/Foreign/Hoppy/Generator/Spec/Callback.hs b/src/Foreign/Hoppy/Generator/Spec/Callback.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Callback.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Callback.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Callback.hs-boot b/src/Foreign/Hoppy/Generator/Spec/Callback.hs-boot
--- a/src/Foreign/Hoppy/Generator/Spec/Callback.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Spec/Callback.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Class.hs b/src/Foreign/Hoppy/Generator/Spec/Class.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Class.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Class.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Class.hs-boot b/src/Foreign/Hoppy/Generator/Spec/Class.hs-boot
--- a/src/Foreign/Hoppy/Generator/Spec/Class.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Spec/Class.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/ClassFeature.hs b/src/Foreign/Hoppy/Generator/Spec/ClassFeature.hs
--- a/src/Foreign/Hoppy/Generator/Spec/ClassFeature.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/ClassFeature.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Computed.hs b/src/Foreign/Hoppy/Generator/Spec/Computed.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Computed.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Computed.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Conversion.hs b/src/Foreign/Hoppy/Generator/Spec/Conversion.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Conversion.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Conversion.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Enum.hs b/src/Foreign/Hoppy/Generator/Spec/Enum.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Enum.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Enum.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Enum.hs-boot b/src/Foreign/Hoppy/Generator/Spec/Enum.hs-boot
--- a/src/Foreign/Hoppy/Generator/Spec/Enum.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Spec/Enum.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Function.hs b/src/Foreign/Hoppy/Generator/Spec/Function.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Function.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Function.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -526,7 +526,7 @@
                                 "Internal error, an exception support module is not available")
                     exceptionSupportModule
                   LH.addImports $ mconcat [hsImport1 "Prelude" "($)", hsImportForRuntime]
-                  return "HoppyFHR.internalHandleExceptions exceptionDb' $"
+                  return "HoppyFHR.internalHandleExceptions exceptionDb' $ "
           else return ""
 
         let callWords = exceptionHandling : hsFnImportedName : map (' ':) convertedArgNames
diff --git a/src/Foreign/Hoppy/Generator/Spec/Function.hs-boot b/src/Foreign/Hoppy/Generator/Spec/Function.hs-boot
--- a/src/Foreign/Hoppy/Generator/Spec/Function.hs-boot
+++ b/src/Foreign/Hoppy/Generator/Spec/Function.hs-boot
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Spec/Variable.hs b/src/Foreign/Hoppy/Generator/Spec/Variable.hs
--- a/src/Foreign/Hoppy/Generator/Spec/Variable.hs
+++ b/src/Foreign/Hoppy/Generator/Spec/Variable.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Types.hs b/src/Foreign/Hoppy/Generator/Types.hs
--- a/src/Foreign/Hoppy/Generator/Types.hs
+++ b/src/Foreign/Hoppy/Generator/Types.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -28,6 +28,9 @@
   fnT,
   fnT',
   -- * Numeric types
+
+  -- NOTE!  When adding a new type here, consider whether a new instance of
+  -- CppDefault is needed in Foreign.Hoppy.Runtime as well.
   boolT,
   boolT',
   charT,
@@ -46,6 +49,9 @@
   floatT',
   doubleT,
   doubleT',
+  char8T,
+  char16T,
+  char32T,
   int8T,
   int16T,
   int32T,
@@ -57,6 +63,8 @@
   ptrdiffT,
   sizeT,
   ssizeT,
+  -- NOTE!  See the start of this export section.
+
   -- ** Custom numeric types
   makeNumericType,
   convertByCoercingIntegral,
@@ -71,6 +79,7 @@
   ) where
 
 import {-# SOURCE #-} qualified Foreign.Hoppy.Generator.Language.Haskell as LH
+--import {-# SOURCE #-} qualified Foreign.Hoppy.Generator.Language.Cpp as LC
 import {-# SOURCE #-} Foreign.Hoppy.Generator.Spec.Callback (callbackT)
 import {-# SOURCE #-} Foreign.Hoppy.Generator.Spec.Class (Class)
 import {-# SOURCE #-} Foreign.Hoppy.Generator.Spec.Enum (enumT)
@@ -247,6 +256,51 @@
       return $ HsTyCon $ UnQual $ HsIdent "Foreign.C.CDouble")
   Nothing BinaryCompatible BinaryCompatible
 
+-- | C++ @char8_t@, Haskell 'Data.Word.Word8'.
+--
+-- Requires C++20.
+char8T :: Type
+char8T =
+  makeNumericTypeWithCppConversion "char8_t" mempty
+  (do LH.addImports hsImportForWord
+      return $ HsTyCon $ UnQual $ HsIdent "HoppyDW.Word8")
+  Nothing  -- No Haskell-side conversion necessary.
+  BinaryCompatible
+  BinaryCompatible
+  (\cppSpec -> cppSpec { conversionSpecCppConversionType = return $ Just word8T })
+
+-- | C++ @char16_t@, Haskell 'Data.Word.Word16'.
+--
+-- This C++ type is the same as @uint_least16_t@ and so it must be at least 16
+-- bits on the C++ side, but may be larger.  Values passed to Haskell will be
+-- shortened to 16 bits.
+char16T :: Type
+char16T =
+  makeNumericTypeWithCppConversion "char16_t"
+  (reqInclude $ includeStd "uchar.h")  -- Only needed pre-C++11, really.
+  (do LH.addImports hsImportForWord
+      return $ HsTyCon $ UnQual $ HsIdent "HoppyDW.Word16")
+  Nothing  -- No Haskell-side conversion necessary.
+  BinaryCompatible
+  BinaryCompatible
+  (\cppSpec -> cppSpec { conversionSpecCppConversionType = return $ Just word16T })
+
+-- | C++ @char32_t@, Haskell 'Data.Word.Word32'.
+--
+-- This C++ type is the same as @uint_least32_t@ and so it must be at least 32
+-- bits on the C++ side, but may be larger.  Values passed to Haskell will be
+-- shortened to 32 bits.
+char32T :: Type
+char32T =
+  makeNumericTypeWithCppConversion "char32_t"
+  (reqInclude $ includeStd "uchar.h")  -- Only needed pre-C++11, really.
+  (do LH.addImports hsImportForWord
+      return $ HsTyCon $ UnQual $ HsIdent "HoppyDW.Word32")
+  Nothing  -- No Haskell-side conversion necessary.
+  BinaryCompatible
+  BinaryCompatible
+  (\cppSpec -> cppSpec { conversionSpecCppConversionType = return $ Just word32T })
+
 -- | C++ @int8_t@, Haskell 'Data.Int.Int8'.
 int8T :: Type
 int8T =
@@ -360,9 +414,44 @@
      -- value.  See 'conversionSpecHaskellFromCppFn'.
   -> Type
 makeNumericType cppName cppReqs hsTypeGen hsCTypeGen convertToCpp convertFromCpp =
+  makeNumericTypeWithCppConversion
+  cppName cppReqs hsTypeGen hsCTypeGen convertToCpp convertFromCpp id
+
+-- | Builds a new numeric type definition, like 'makeNumericType', but allows
+-- for adding additional behaviour to the 'ConversionSpecCpp'.
+makeNumericTypeWithCppConversion ::
+     String
+     -- ^ The name of the C++ type.
+  -> Reqs
+     -- ^ Includes necessary to use the C++ type.
+  -> LH.Generator HsType
+     -- ^ Generator for rendering the Haskell type to be used, along with any
+     -- required imports.  See 'conversionSpecHaskellHsType'.
+  -> Maybe (LH.Generator HsType)
+     -- ^ If there is a Haskell type distinct from the previous argument to be
+     -- used for passing over the FFI boundary, then provide it here.  See
+     -- 'conversionSpecHaskellCType'.
+  -> ConversionMethod (LH.Generator ())
+     -- ^ Method to use to convert a Haskell value to a value to be passed over
+     -- the FFI.  See 'conversionSpecHaskellToCppFn'.
+  -> ConversionMethod (LH.Generator ())
+     -- ^ Method to use to convert a value received over the FFI into a Haskell
+     -- value.  See 'conversionSpecHaskellFromCppFn'.
+  -> (ConversionSpecCpp -> ConversionSpecCpp)
+     -- ^ Function to add additional conversion behaviour to the C++ side of the
+     -- type.
+  -> Type
+makeNumericTypeWithCppConversion cppName
+                                 cppReqs
+                                 hsTypeGen
+                                 hsCTypeGen
+                                 convertToCpp
+                                 convertFromCpp
+                                 modifyCppConversion =
   Internal_TManual spec
   where spec =
-          (makeConversionSpec cppName $ makeConversionSpecCpp cppName $ return cppReqs)
+          (makeConversionSpec cppName $ modifyCppConversion $
+           makeConversionSpecCpp cppName $ return cppReqs)
           { conversionSpecHaskell =
               Just $ makeConversionSpecHaskell
                 hsTypeGen
diff --git a/src/Foreign/Hoppy/Generator/Util.hs b/src/Foreign/Hoppy/Generator/Util.hs
--- a/src/Foreign/Hoppy/Generator/Util.hs
+++ b/src/Foreign/Hoppy/Generator/Util.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
diff --git a/src/Foreign/Hoppy/Generator/Version.hs b/src/Foreign/Hoppy/Generator/Version.hs
--- a/src/Foreign/Hoppy/Generator/Version.hs
+++ b/src/Foreign/Hoppy/Generator/Version.hs
@@ -1,6 +1,6 @@
 -- This file is part of Hoppy.
 --
--- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
+-- Copyright 2015-2024 Bryan Gardiner <bog@khumba.net>
 --
 -- This program is free software: you can redistribute it and/or modify
 -- it under the terms of the GNU Affero General Public License as published by
@@ -72,6 +72,7 @@
     Cpp1998
   | Cpp2011
   | Cpp2014
+  | Cpp2020
   deriving (Bounded, Enum, Eq, Ord, Show)
 
 -- | The 'CppVersion' chosen when one is not explicitly requested.  This is
@@ -80,10 +81,10 @@
 defaultCppVersion = Cpp2011
 
 -- | The active version of the C++ standard.  This looks to the @HOPPY_CPP_STD@
--- environment variable, and accepts the values @c++98@, @c++11@, and @c++14@,
--- which map to the corresponding 'CppVersion' values.  If a value other than
--- these is set, then a warning is printed and the default is used.  If no value
--- is set, the default is used.
+-- environment variable, and accepts the values @c++98@, @c++11@, @c++14@, and
+-- @c++20@, which map to the corresponding 'CppVersion' values.  If a value
+-- other than these is set, then a warning is printed and the default is used.
+-- If no value is set, the default is used.
 --
 -- This uses 'unsafePerformIO' internally and won't cope with a changing
 -- environment.
@@ -97,5 +98,6 @@
     Just "c++98" -> return Cpp1998
     Just "c++11" -> return Cpp2011
     Just "c++14" -> return Cpp2014
+    Just "c++20" -> return Cpp2020
     Just str -> do hPutStrLn stderr $ "Warning: Invalid HOPPY_CPP_STD value " ++ show str ++ "."
                    return defaultCppVersion
