Add support for boost::variant in C++11 mode

In C++11 mode, `boost::apply_visitor` requires an explicit `result_type`.
This also adds optional tests for `boost::variant` in C++11/14, if boost
is available. In C++17 mode, `std::variant` is tested instead.
diff --git a/.appveyor.yml b/.appveyor.yml
index 8d30cf3..b66b8e5 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -28,8 +28,12 @@
 install:
 - ps: |
     if ($env:PLATFORM -eq "x64") { $env:CMAKE_ARCH = "x64" }
-    if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") { $env:CMAKE_GENERATOR = "Visual Studio 15 2017" }
-    else { $env:CMAKE_GENERATOR = "Visual Studio 14 2015" }
+    if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") {
+      $env:CMAKE_GENERATOR = "Visual Studio 15 2017"
+      $env:CMAKE_INCLUDE_PATH = "C:\Libraries\boost_1_64_0"
+    } else {
+      $env:CMAKE_GENERATOR = "Visual Studio 14 2015"
+    }
     if ($env:PYTHON) {
       if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" }
       $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH"
@@ -45,7 +49,7 @@
 - ps: |
     Start-FileDownload 'http://bitbucket.org/eigen/eigen/get/3.3.3.zip'
     7z x 3.3.3.zip -y > $null
-    $env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f"
+    $env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f;$env:CMAKE_INCLUDE_PATH"
 build_script:
 - cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%"
     -DPYBIND11_CPP_STANDARD=/std:c++%CPP%
diff --git a/.travis.yml b/.travis.yml
index f6ee9af..ae64317 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -150,7 +150,7 @@
     $SCRIPT_RUN_PREFIX sh -c "for s in 0 15; do sleep \$s; \
       apt-get -qy --no-install-recommends install \
         $PY_DEBUG python$PYTHON-dev python$PY-pytest python$PY-scipy \
-        libeigen3-dev cmake make ${COMPILER_PACKAGES} && break; done"
+        libeigen3-dev libboost-dev cmake make ${COMPILER_PACKAGES} && break; done"
   else
 
     if [ "$CLANG" = "4.0" ]; then
diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst
index ac6318f..2d709d7 100644
--- a/docs/advanced/cast/stl.rst
+++ b/docs/advanced/cast/stl.rst
@@ -60,8 +60,7 @@
         template <>
         struct visit_helper<boost::variant> {
             template <typename... Args>
-            static auto call(Args &&...args)
-                -> decltype(boost::apply_visitor(args...)) {
+            static auto call(Args &&...args) -> decltype(boost::apply_visitor(args...)) {
                 return boost::apply_visitor(args...);
             }
         };
@@ -71,6 +70,13 @@
 a ``name::visit()`` function. For any other function name, the specialization must be
 included to tell pybind11 how to visit the variant.
 
+.. note::
+
+    pybind11 only supports the modern implementation of ``boost::variant``
+    which makes use of variadic templates. This requires Boost 1.56 or newer.
+    Additionally, on Windows, MSVC 2017 is required because ``boost::variant``
+    falls back to the old non-variadic implementation on MSVC 2015.
+
 .. _opaque:
 
 Making opaque types
diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h
index d07a81f..c8c9e90 100644
--- a/include/pybind11/stl.h
+++ b/include/pybind11/stl.h
@@ -292,8 +292,10 @@
     return_value_policy policy;
     handle parent;
 
+    using result_type = handle; // required by boost::variant in C++11
+
     template <typename T>
-    handle operator()(T &&src) const {
+    result_type operator()(T &&src) const {
         return make_caster<T>::cast(std::forward<T>(src), policy, parent);
     }
 };
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index aa2704b..18e6b9f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -112,6 +112,9 @@
   endif()
 endif()
 
+# Optional dependency for some tests
+find_package(Boost)
+
 # Compile with compiler warnings turned on
 function(pybind11_enable_warnings target_name)
   if(MSVC)
@@ -141,37 +144,40 @@
 endforeach()
 
 set(testdir ${CMAKE_CURRENT_SOURCE_DIR})
-foreach(tgt ${test_targets})
+foreach(target ${test_targets})
   set(test_files ${PYBIND11_TEST_FILES})
-  if(NOT tgt STREQUAL "pybind11_tests")
+  if(NOT target STREQUAL "pybind11_tests")
     set(test_files "")
   endif()
 
   # Create the binding library
-  pybind11_add_module(${tgt} THIN_LTO ${tgt}.cpp
-    ${test_files} ${PYBIND11_HEADERS})
-
-  pybind11_enable_warnings(${tgt})
+  pybind11_add_module(${target} THIN_LTO ${target}.cpp ${test_files} ${PYBIND11_HEADERS})
+  pybind11_enable_warnings(${target})
 
   if(MSVC)
-    target_compile_options(${tgt} PRIVATE /utf-8)
+    target_compile_options(${target} PRIVATE /utf-8)
   endif()
 
   if(EIGEN3_FOUND)
     if (PYBIND11_EIGEN_VIA_TARGET)
-      target_link_libraries(${tgt} PRIVATE Eigen3::Eigen)
+      target_link_libraries(${target} PRIVATE Eigen3::Eigen)
     else()
-      target_include_directories(${tgt} PRIVATE ${EIGEN3_INCLUDE_DIR})
+      target_include_directories(${target} PRIVATE ${EIGEN3_INCLUDE_DIR})
     endif()
-    target_compile_definitions(${tgt} PRIVATE -DPYBIND11_TEST_EIGEN)
+    target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_EIGEN)
+  endif()
+
+  if(Boost_FOUND)
+    target_include_directories(${target} PRIVATE ${Boost_INCLUDE_DIRS})
+    target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST)
   endif()
 
   # Always write the output file directly into the 'tests' directory (even on MSVC)
   if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
-    set_target_properties(${tgt} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${testdir})
+    set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${testdir})
     foreach(config ${CMAKE_CONFIGURATION_TYPES})
       string(TOUPPER ${config} config)
-      set_target_properties(${tgt} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} ${testdir})
+      set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} ${testdir})
     endforeach()
   endif()
 endforeach()
diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp
index 93e8c66..66fe9d1 100644
--- a/tests/test_stl.cpp
+++ b/tests/test_stl.cpp
@@ -10,6 +10,27 @@
 #include "pybind11_tests.h"
 #include <pybind11/stl.h>
 
+// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14
+#if PYBIND11_HAS_VARIANT
+using std::variant;
+#elif PYBIND11_TEST_BOOST
+#  include <boost/variant.hpp>
+#  define PYBIND11_HAS_VARIANT 1
+using boost::variant;
+
+namespace pybind11 { namespace detail {
+template <typename... Ts>
+struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};
+
+template <>
+struct visit_helper<boost::variant> {
+    template <typename... Args>
+    static auto call(Args &&...args) -> decltype(boost::apply_visitor(args...)) {
+        return boost::apply_visitor(args...);
+    }
+};
+}} // namespace pybind11::detail
+#endif
 
 /// Issue #528: templated constructor
 struct TplCtorClass {
@@ -162,22 +183,27 @@
 #endif
 
 #ifdef PYBIND11_HAS_VARIANT
+    static_assert(std::is_same<py::detail::variant_caster_visitor::result_type, py::handle>::value,
+                  "visitor::result_type is required by boost::variant in C++11 mode");
+
     struct visitor {
-        const char *operator()(int) { return "int"; }
-        const char *operator()(std::string) { return "std::string"; }
-        const char *operator()(double) { return "double"; }
-        const char *operator()(std::nullptr_t) { return "std::nullptr_t"; }
+        using result_type = const char *;
+
+        result_type operator()(int) { return "int"; }
+        result_type operator()(std::string) { return "std::string"; }
+        result_type operator()(double) { return "double"; }
+        result_type operator()(std::nullptr_t) { return "std::nullptr_t"; }
     };
 
     // test_variant
-    m.def("load_variant", [](std::variant<int, std::string, double, std::nullptr_t> v) {
-        return std::visit(visitor(), v);
+    m.def("load_variant", [](variant<int, std::string, double, std::nullptr_t> v) {
+        return py::detail::visit_helper<variant>::call(visitor(), v);
     });
-    m.def("load_variant_2pass", [](std::variant<double, int> v) {
-        return std::visit(visitor(), v);
+    m.def("load_variant_2pass", [](variant<double, int> v) {
+        return py::detail::visit_helper<variant>::call(visitor(), v);
     });
     m.def("cast_variant", []() {
-        using V = std::variant<int, std::string>;
+        using V = variant<int, std::string>;
         return py::make_tuple(V(5), V("Hello"));
     });
 #endif