diff --git a/source/adios2/ADIOSMacros.h b/source/adios2/ADIOSMacros.h
index 2850171ef78e9cb36445144c3b1658d5eeb52349..ee018ad18064f13090f0008a9e4c93ad7cc420b4 100644
--- a/source/adios2/ADIOSMacros.h
+++ b/source/adios2/ADIOSMacros.h
@@ -84,4 +84,61 @@
     MACRO(double)                                                              \
     MACRO(long double)
 
+// The ADIOS_FOREACH_TYPE_2ARGS macro assumes the given argument is a macro
+// which takes two arguments, the first being a type, and the second being a
+// label for that type, i.e. std::complex<float> and CFloat.
+//
+// An example of this might be to define a virtual method for each known
+// type, and since the method is virtual then it cannot be a template.
+// For example:
+//
+//   #define declare_foo(T,L) virtual const T& foo ## L (std::string bar);
+//   ADIOS_FOREACH_TYPE_2ARGS(declare_foo)
+//   #undef declare_foo
+//
+//   is equivalent to:
+//
+//   virtual           char& foo_Char(std::string bar);
+//   virtual unsigned  char& foo_UChar(std::string bar);
+//   virtual          short& foo_Short(std::string bar);
+//   virtual unsigned short& foo_UShort(std::string bar);
+//   ...
+//   virtual std::complex<long double>& foo_CLDouble(std::string bar);
+//
+#define ADIOS2_FOREACH_TYPE_2ARGS(MACRO)                                       \
+    MACRO(char, Char)                                                          \
+    MACRO(unsigned char, UChar)                                                \
+    MACRO(short, Short)                                                        \
+    MACRO(unsigned short, UShort)                                              \
+    MACRO(int, Int)                                                            \
+    MACRO(unsigned int, UInt)                                                  \
+    MACRO(long int, LInt)                                                      \
+    MACRO(long long int, LLInt)                                                \
+    MACRO(unsigned long int, ULInt)                                            \
+    MACRO(unsigned long long int, ULLInt)                                      \
+    MACRO(float, Float)                                                        \
+    MACRO(double, Double)                                                      \
+    MACRO(long double, LDouble)                                                \
+    MACRO(std::complex<float>, CFloat)                                         \
+    MACRO(std::complex<double>, CDouble)                                       \
+    MACRO(std::complex<long double>, CLDouble)
+
+#define ADIOS2_FOREACH_PRIMITIVE_TYPE_2ARGS(MACRO)                             \
+    MACRO(char, Char)                                                          \
+    MACRO(unsigned char, UChar)                                                \
+    MACRO(short, Short)                                                        \
+    MACRO(unsigned short, UShort)                                              \
+    MACRO(int, Int)                                                            \
+    MACRO(unsigned int, UInt)                                                  \
+    MACRO(long int, LInt)                                                      \
+    MACRO(long long int, LLInt)                                                \
+    MACRO(unsigned long int, ULInt)                                            \
+    MACRO(unsigned long long int, ULLInt)                                      \
+    MACRO(float, Float)                                                        \
+    MACRO(double, Double)
+
+#define ADIOS2_FOREACH_COMPLEX_TYPE_2ARGS(MACRO)                               \
+    MACRO(std::complex<float>, CFloat)                                         \
+    MACRO(std::complex<double>, CDouble)
+
 #endif /* ADIOS2_ADIOSMACROS_H */
diff --git a/source/adios2/core/Engine.cpp b/source/adios2/core/Engine.cpp
index 82735b91c813387466f8c7ed563e7e3229a76652..9d19e892b7d357e35e1bbe3ef2de05ff4377df5c 100644
--- a/source/adios2/core/Engine.cpp
+++ b/source/adios2/core/Engine.cpp
@@ -128,88 +128,15 @@ VariableBase *Engine::InquireVariableUnknown(const std::string &name,
 {
     return nullptr;
 }
-Variable<char> *Engine::InquireVariableChar(const std::string &name,
-                                            const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned char> *Engine::InquireVariableUChar(const std::string &name,
-                                                      const bool readIn)
-{
-    return nullptr;
-}
-Variable<short> *Engine::InquireVariableShort(const std::string &name,
-                                              const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned short> *Engine::InquireVariableUShort(const std::string &name,
-                                                        const bool readIn)
-{
-    return nullptr;
-}
-Variable<int> *Engine::InquireVariableInt(const std::string &name,
-                                          const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned int> *Engine::InquireVariableUInt(const std::string &name,
-                                                    const bool readIn)
-{
-    return nullptr;
-}
-Variable<long int> *Engine::InquireVariableLInt(const std::string &name,
-                                                const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned long int> *
-Engine::InquireVariableULInt(const std::string &name, const bool readIn)
-{
-    return nullptr;
-}
-Variable<long long int> *Engine::InquireVariableLLInt(const std::string &name,
-                                                      const bool readIn)
-{
-    return nullptr;
-}
-Variable<unsigned long long int> *
-Engine::InquireVariableULLInt(const std::string &name, const bool readIn)
-{
-    return nullptr;
-}
 
-Variable<float> *Engine::InquireVariableFloat(const std::string &name,
-                                              const bool readIn)
-{
-    return nullptr;
-}
-Variable<double> *Engine::InquireVariableDouble(const std::string &name,
-                                                const bool readIn)
-{
-    return nullptr;
-}
-Variable<long double> *Engine::InquireVariableLDouble(const std::string &name,
-                                                      const bool readIn)
-{
-    return nullptr;
-}
-Variable<cfloat> *Engine::InquireVariableCFloat(const std::string &name,
-                                                const bool readIn)
-{
-    return nullptr;
-}
-Variable<cdouble> *Engine::InquireVariableCDouble(const std::string &name,
-                                                  const bool readIn)
-{
-    return nullptr;
-}
-
-Variable<cldouble> *Engine::InquireVariableCLDouble(const std::string &name,
-                                                    const bool readIn)
-{
-    return nullptr;
-}
+#define define(T, L)                                                           \
+    Variable<T> *Engine::InquireVariable##L(const std::string &name,           \
+                                            const bool readIn)                 \
+    {                                                                          \
+        return nullptr;                                                        \
+    }
+ADIOS2_FOREACH_TYPE_2ARGS(define)
+#undef define
 
 #define declare_type(T)                                                        \
     void Engine::DoScheduleRead(Variable<T> &variable, const T *values)        \
diff --git a/source/adios2/core/Engine.h b/source/adios2/core/Engine.h
index c07a6b8ba621ee9944763cbd34a0b3fe028493d5..8218fff22b5dd17d323838d06381c3eeeabaee8f 100644
--- a/source/adios2/core/Engine.h
+++ b/source/adios2/core/Engine.h
@@ -356,40 +356,11 @@ protected:
     // READ
     virtual VariableBase *InquireVariableUnknown(const std::string &name,
                                                  const bool readIn);
-    virtual Variable<char> *InquireVariableChar(const std::string &name,
-                                                const bool readIn);
-    virtual Variable<unsigned char> *
-    InquireVariableUChar(const std::string &name, const bool readIn);
-    virtual Variable<short> *InquireVariableShort(const std::string &name,
-                                                  const bool readIn);
-    virtual Variable<unsigned short> *
-    InquireVariableUShort(const std::string &name, const bool readIn);
-    virtual Variable<int> *InquireVariableInt(const std::string &name,
-                                              const bool readIn);
-    virtual Variable<unsigned int> *InquireVariableUInt(const std::string &name,
-                                                        const bool readIn);
-    virtual Variable<long int> *InquireVariableLInt(const std::string &name,
-                                                    const bool readIn);
-    virtual Variable<unsigned long int> *
-    InquireVariableULInt(const std::string &name, const bool readIn);
-    virtual Variable<long long int> *
-    InquireVariableLLInt(const std::string &name, const bool readIn);
-    virtual Variable<unsigned long long int> *
-    InquireVariableULLInt(const std::string &name, const bool readIn);
-
-    virtual Variable<float> *InquireVariableFloat(const std::string &name,
-                                                  const bool readIn);
-    virtual Variable<double> *InquireVariableDouble(const std::string &name,
-                                                    const bool readIn);
-    virtual Variable<long double> *
-    InquireVariableLDouble(const std::string &name, const bool readIn);
-
-    virtual Variable<cfloat> *InquireVariableCFloat(const std::string &name,
-                                                    const bool readIn);
-    virtual Variable<cdouble> *InquireVariableCDouble(const std::string &name,
-                                                      const bool readIn);
-    virtual Variable<cldouble> *InquireVariableCLDouble(const std::string &name,
-                                                        const bool readIn);
+#define declare(T, L)                                                          \
+    virtual Variable<T> *InquireVariable##L(const std::string &name,           \
+                                            const bool readIn);
+    ADIOS2_FOREACH_TYPE_2ARGS(declare)
+#undef declare
 
 // Known-type
 #define declare_type(T)                                                        \