From 85245fe663953d20d1f84785936e47f09d8b757d Mon Sep 17 00:00:00 2001
From: Stephan Seitz <stephan.seitz@fau.de>
Date: Fri, 8 Nov 2019 20:30:11 +0100
Subject: [PATCH] Add walberla::Variant

---
 src/core/Variant.h         | 62 +++++++++++++++++++++++++++++
 tests/core/CMakeLists.txt  |  3 ++
 tests/core/VariantTest.cpp | 81 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 146 insertions(+)
 create mode 100644 src/core/Variant.h
 create mode 100644 tests/core/VariantTest.cpp

diff --git a/src/core/Variant.h b/src/core/Variant.h
new file mode 100644
index 000000000..6e4d580e4
--- /dev/null
+++ b/src/core/Variant.h
@@ -0,0 +1,62 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) OPTIONAL later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  OPTIONAL WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file Variant.h
+//! \ingroup core
+//! \author Stephan Seitz <stephan.seitz@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+
+#if __cpp_lib_variant
+#define WALBERLA_USE_STD_VARIANT
+#endif
+
+#if defined(WALBERLA_USE_STD_VARIANT)
+#include <variant>
+#else
+#include <boost/variant.hpp>
+#endif
+
+
+
+namespace walberla
+{
+
+#if defined(WALBERLA_USE_STD_VARIANT)
+using std::variant;
+using std::visit;
+using std::get;
+using std::holds_alternative;
+using std::bad_variant_access;
+#else
+using boost::variant;
+using boost::get;
+template <class T, class... Types>
+constexpr bool holds_alternative( const boost::variant<Types...>& v ) noexcept
+{
+   return v.type() == typeid( T );
+}
+using bad_variant_access = boost::bad_get;
+template<typename Visitor, typename... Variant>
+decltype( auto ) visit( Visitor&& visitor, Variant&& ... variant )
+{
+   return boost::apply_visitor( visitor, variant... );
+}
+#endif
+
+}
diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt
index 185fc8be0..fa77e164a 100644
--- a/tests/core/CMakeLists.txt
+++ b/tests/core/CMakeLists.txt
@@ -209,6 +209,9 @@ waLBerla_execute_test( NAME UNIQUEID PROCESSES 4)
 waLBerla_compile_test( FILES VersionTest.cpp )
 waLBerla_execute_test( NAME VersionTest )
 
+waLBerla_compile_test( FILES VariantTest )
+waLBerla_execute_test( NAME VariantTest )
+
 ##################
 # load_balancing #
 ##################
diff --git a/tests/core/VariantTest.cpp b/tests/core/VariantTest.cpp
new file mode 100644
index 000000000..a879b5b98
--- /dev/null
+++ b/tests/core/VariantTest.cpp
@@ -0,0 +1,81 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file VariantTest.cpp
+//! \ingroup core
+//! \author Step
+//! \brief Test if there are no typos in the collective header files "all.h"
+//
+//======================================================================================================================
+
+#include "core/Variant.h"
+#include "core/debug/all.h"
+
+#include <string>
+#include <cassert>
+#include <iostream>
+#include <vector>
+
+using namespace std::literals; // C++ 14
+using var_t = walberla::variant<int, long, double, std::string>;
+
+int main( int /*argc*/, char** /*argv*/ )
+{
+   walberla::debug::enterTestMode();
+
+
+
+   // Example from: https://en.cppreference.com/w/cpp/utility/variant (license (CC-BY-SA) and GPL)
+   walberla::variant<int, float> v, w;
+   v = 12; // v contains int
+   int i = walberla::get<int>( v );
+#ifdef WALBERLA_USE_STD_VARIANT
+   WALBERLA_CHECK( i == 12 ); // boost::variant cannot use == with variant and type
+#endif
+   WALBERLA_CHECK( 0 == 12 - i );
+   w = walberla::get<int>( v );
+   w = v;
+#ifdef WALBERLA_USE_STD_VARIANT
+   WALBERLA_CHECK( w == 12 ); // boost::variant cannot use == with variant and type
+#endif
+   WALBERLA_CHECK( 0 == 12 - i );
+
+   //  walberla::get<double>(v); // error: no double in [int, float]
+   //  walberla::get<3>(v);      // error: valid index values are 0 and 1
+
+   try {
+      walberla::get<float>( w ); // w contains int, not float: will throw
+   } catch ( const walberla::bad_variant_access& ) {}
+
+   walberla::variant<std::string> x( "abc" ); // converting constructors work when unambiguous
+   x = "def"; // converting assignment also works when unambiguous
+
+   std::cout << "hallo" << std::endl;
+   walberla::variant<std::string, bool> y( "abc" ); // casts to bool when passed a char const *
+   std::cout << "eoo" << std::endl;
+   WALBERLA_CHECK( walberla::holds_alternative<bool>( y ) ); // succeeds
+   y = "xyz"s;
+   WALBERLA_CHECK( walberla::holds_alternative<std::string>( y ) ); //succeeds
+
+   std::cout << "bye" << std::endl;
+   std::vector<var_t> vec {10, 15l, 1.5, "hello"};
+
+   for ( auto& z : vec ) {
+      // 1. void visitor, only called for side-effects (here, for I/O)
+      walberla::visit( []( auto && arg ) {std::cout << arg;}, z );
+   }
+
+   return 0;
+}
-- 
GitLab