title: How to wrap C++17 std::variant with SWIG for use in python?
body: <p>I am trying to wrap in Python with SWIG some C++17 code that uses std::variant.</p>
  <p>I've found this answer (<a href="https://stackoverflow.com/a/58513139">https://stackoverflow.com/a/58513139</a>) about wrapping boost::variant and I managed to adapt the code so that it works with std::variant instead. However, according to the specification of the answer, the code should work so that 'anywhere a C++ function takes a boost::variant we should transparently accept any of the types the variant can hold for that function argument'. This requirement seems to only be satisfied when a const reference to a std::variant is used. For example if in my c++ file dummy.cpp I have</p>
  <pre><code>void foo(std::variant&lt;int, double&gt; value)
  {
    std::cout &lt;&lt; &quot;foo&quot; &lt;&lt; std::endl;
  }
  
  void bar(const std::variant&lt;int, double&gt;&amp; value)
  {
    std::cout &lt;&lt; &quot;bar&quot; &lt;&lt; std::endl;
  }
  
  </code></pre>
  <p>then in my dummy.i</p>
  <pre><code>%include &quot;variant.i&quot; # My customization of boost_variant.i
  %std_variant(DummyVariant, int, double); # Creating the variant type for SWIG/python
  </code></pre>
  <p>when I try to use it from a Python script the following works</p>
  <pre><code>from dummy import bar
  bar(5)
  </code></pre>
  <p>this, however, does not:</p>
  <pre><code>from dummy import foo
  foo(5)
  </code></pre>
  <p>I get: TypeError: in method 'foo', argument 1 of type 'std::variant&lt; int,double &gt;'.</p>
  <p>Does anybody have an idea of what is missing in my variant.i file to make this works as expected? This is the file I have:</p>
  <pre><code>%{
  #include &lt;variant&gt;
  
  static PyObject *this_module = NULL;
  %}
  
  %init %{
    // We need to &quot;borrow&quot; a reference to this for our typemaps to be able to look up the right functions
    this_module = m; // borrow should be fine since we can only get called when our module is loaded right?
    // Wouldn't it be nice if $module worked *anywhere*
  %}
  
  #define FE_0(...)
  #define FE_1(action,a1) action(0,a1)
  #define FE_2(action,a1,a2) action(0,a1); action(1,a2)
  #define FE_3(action,a1,a2,a3) action(0,a1); action(1,a2); action(2,a3)
  #define FE_4(action,a1,a2,a3,a4) action(0,a1); action(1,a2); action(2,a3); action(3,a4)
  #define FE_5(action,a1,a2,a3,a4,a5) action(0,a1); action(1,a2); action(2,a3); action(3,a4); action(4,a5)
  
  #define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME
  %define FOR_EACH(action,...)
    GET_MACRO(__VA_ARGS__, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0)(action,__VA_ARGS__)
  %enddef
  
  #define in_helper(num,type) const type &amp; convert_type ## num () { return std::get&lt;type&gt;(*$self); }
  #define constructor_helper(num,type) variant(const type&amp;)
  
  %define %std_variant(Name, ...)
  
  %rename(Name) std::variant&lt;__VA_ARGS__&gt;;
  
  namespace std {
    struct variant&lt;__VA_ARGS__&gt; {
      variant();
      variant(const std::variant&lt;__VA_ARGS__&gt;&amp;);
      FOR_EACH(constructor_helper, __VA_ARGS__);
      int index();
  
      %extend {
        FOR_EACH(in_helper, __VA_ARGS__);
      }
    };
  }
  
  %typemap(out) std::variant&lt;__VA_ARGS__&gt; {
    // Make our function output into a PyObject
    PyObject *tmp = SWIG_NewPointerObj(&amp;$1, $&amp;1_descriptor, 0); // Python does not own this object...
  
    // Pass that temporary PyObject into the helper function and get another PyObject back in exchange
    const std::string func_name = &quot;convert_type&quot; + std::to_string($1.index());
    $result = PyObject_CallMethod(tmp, func_name.c_str(),  &quot;&quot;);
    Py_DECREF(tmp);
  }
  
  %typemap(in) const std::variant&lt;__VA_ARGS__&gt;&amp; (PyObject *tmp=NULL) {
    // I don't much like having to &quot;guess&quot; the name of the make_variant we want to use here like this...
    // But it's hard to support both -builtin and regular modes and generically find the right code.
    PyObject *helper_func = PyObject_GetAttrString(this_module, &quot;new_&quot; #Name );
    assert(helper_func);
    // TODO: is O right, or should it be N?
    tmp = PyObject_CallFunction(helper_func, &quot;O&quot;, $input);
    Py_DECREF(helper_func);
    if (!tmp) SWIG_fail; // An exception is already pending
  
    // TODO: if we cared, we chould short-circuit things a lot for the case where our input really was a variant object
    const int res = SWIG_ConvertPtr(tmp, (void**)&amp;$1, $1_descriptor, 0);
    if (!SWIG_IsOK(res)) {
      SWIG_exception_fail(SWIG_ArgError(res), &quot;Variant typemap failed, not sure if this can actually happen&quot;);
    }
  }
  
  %typemap(freearg) const std::variant&lt;__VA_ARGS__&gt;&amp; %{
    Py_DECREF(tmp$argnum);
  %}
  
  %enddef
  </code></pre>
  <p>which is taken almost <em>verbatim</em> from Flexo's answer, save a few changes to make it work with std::variant instead of boost::variant.</p>