Skip to content

Commit c1e8d14

Browse files
authored
[embind] Require C++17 or newer. (#25773)
As announced in #24850 and on the [mailing list](https://groups.google.com/g/emscripten-discuss/c/BSLc5y2aKDY) we are going to require C++17 or newer for embind.
1 parent 6f87785 commit c1e8d14

File tree

5 files changed

+20
-95
lines changed

5 files changed

+20
-95
lines changed

system/include/emscripten/bind.h

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@
77

88
#pragma once
99

10+
#if __cplusplus < 201703L
11+
#error "embind requires -std=c++17 or newer"
12+
#endif
13+
1014
#include <cassert>
1115
#include <cstddef>
1216
#include <functional>
1317
#include <map>
1418
#include <string>
1519
#include <type_traits>
1620
#include <vector>
17-
#if __cplusplus >= 201703L
1821
#include <optional>
19-
#endif
2022

2123
#include <emscripten/em_asm.h>
2224
#include <emscripten/val.h>
@@ -1591,7 +1593,7 @@ class class_ {
15911593
// overload.
15921594
typename = typename std::enable_if<
15931595
!std::is_function<FieldType>::value &&
1594-
internal::conjunction<internal::isPolicy<Policies>...>::value>::type>
1596+
std::conjunction<internal::isPolicy<Policies>...>::value>::type>
15951597
EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, const FieldType ClassType::*field, Policies...) const {
15961598
using namespace internal;
15971599
using ReturnPolicy = GetReturnValuePolicy<FieldType, Policies...>::tag;
@@ -1619,7 +1621,7 @@ class class_ {
16191621
// overload.
16201622
typename = typename std::enable_if<
16211623
!std::is_function<FieldType>::value &&
1622-
internal::conjunction<internal::isPolicy<Policies>...>::value>::type>
1624+
std::conjunction<internal::isPolicy<Policies>...>::value>::type>
16231625
EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, FieldType ClassType::*field, Policies...) const {
16241626
using namespace internal;
16251627
using ReturnPolicy = GetReturnValuePolicy<FieldType, Policies...>::tag;
@@ -1648,7 +1650,7 @@ class class_ {
16481650
// Prevent the template from wrongly matching the getter/setter overload
16491651
// of this function.
16501652
typename = typename std::enable_if<
1651-
internal::conjunction<internal::isPolicy<Policies>...>::value>::type>
1653+
std::conjunction<internal::isPolicy<Policies>...>::value>::type>
16521654
EMSCRIPTEN_ALWAYS_INLINE const class_& property(const char* fieldName, Getter getter, Policies...) const {
16531655
using namespace internal;
16541656

@@ -1775,7 +1777,6 @@ class class_ {
17751777
}
17761778
};
17771779

1778-
#if __cplusplus >= 201703L
17791780
template<typename T>
17801781
void register_optional() {
17811782
// Optional types are automatically registered for some internal types so
@@ -1790,7 +1791,6 @@ void register_optional() {
17901791
internal::TypeID<std::optional<T>>::get(),
17911792
internal::TypeID<typename std::remove_pointer<T>::type>::get());
17921793
}
1793-
#endif
17941794

17951795
////////////////////////////////////////////////////////////////////////////////
17961796
// VECTORS
@@ -1800,9 +1800,6 @@ namespace internal {
18001800

18011801
template<typename VectorType>
18021802
struct VectorAccess {
1803-
// This nearly duplicated code is used for generating more specific TypeScript
1804-
// types when using more modern C++ versions.
1805-
#if __cplusplus >= 201703L
18061803
static std::optional<typename VectorType::value_type> get(
18071804
const VectorType& v,
18081805
unsigned int index
@@ -1813,18 +1810,6 @@ struct VectorAccess {
18131810
return {};
18141811
}
18151812
}
1816-
#else
1817-
static val get(
1818-
const VectorType& v,
1819-
unsigned int index
1820-
) {
1821-
if (index < v.size()) {
1822-
return val(v[index], allow_raw_pointers());
1823-
} else {
1824-
return val::undefined();
1825-
}
1826-
}
1827-
#endif
18281813

18291814
static bool set(
18301815
VectorType& v,
@@ -1860,9 +1845,7 @@ struct VectorAccess {
18601845
template<typename T, class Allocator=std::allocator<T>>
18611846
class_<std::vector<T, Allocator>> register_vector(const char* name) {
18621847
typedef std::vector<T, Allocator> VecType;
1863-
#if __cplusplus >= 201703L
18641848
register_optional<T>();
1865-
#endif
18661849

18671850
return class_<VecType>(name)
18681851
.template constructor<>()
@@ -1882,9 +1865,6 @@ namespace internal {
18821865

18831866
template<typename MapType>
18841867
struct MapAccess {
1885-
// This nearly duplicated code is used for generating more specific TypeScript
1886-
// types when using more modern C++ versions.
1887-
#if __cplusplus >= 201703L
18881868
static std::optional<typename MapType::mapped_type> get(
18891869
const MapType& m,
18901870
const typename MapType::key_type& k
@@ -1896,19 +1876,6 @@ struct MapAccess {
18961876
return i->second;
18971877
}
18981878
}
1899-
#else
1900-
static val get(
1901-
const MapType& m,
1902-
const typename MapType::key_type& k
1903-
) {
1904-
auto i = m.find(k);
1905-
if (i == m.end()) {
1906-
return val::undefined();
1907-
} else {
1908-
return val(i->second);
1909-
}
1910-
}
1911-
#endif
19121879

19131880
static void set(
19141881
MapType& m,
@@ -1940,9 +1907,7 @@ template<typename K, typename V, class Compare = std::less<K>,
19401907
class Allocator = std::allocator<std::pair<const K, V>>>
19411908
class_<std::map<K, V, Compare, Allocator>> register_map(const char* name) {
19421909
typedef std::map<K,V, Compare, Allocator> MapType;
1943-
#if __cplusplus >= 201703L
19441910
register_optional<V>();
1945-
#endif
19461911

19471912
return class_<MapType>(name)
19481913
.template constructor<>()
@@ -1957,7 +1922,6 @@ class_<std::map<K, V, Compare, Allocator>> register_map(const char* name) {
19571922
// std::optional
19581923
////////////////////////////////////////////////////////////////////////////////
19591924

1960-
#if __cplusplus >= 201703L
19611925
namespace internal {
19621926
template <typename T>
19631927
struct BindingType<std::optional<T>> {
@@ -1982,7 +1946,6 @@ struct BindingType<std::optional<T>> {
19821946
}
19831947
};
19841948
} // end namespace internal
1985-
#endif
19861949

19871950

19881951
////////////////////////////////////////////////////////////////////////////////

system/include/emscripten/val.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,6 @@ class EMBIND_VISIBILITY_DEFAULT val {
573573
template<internal::EM_INVOKER_KIND Kind, typename Ret, typename... Args>
574574
static Ret internalCall(EM_VAL handle, const char *methodName, Args&&... args) {
575575
using namespace internal;
576-
#if __cplusplus >= 201703L
577576
using Policy = WithPolicies<FilterTypes<isPolicy, Args...>>;
578577
auto filteredArgs = Filter<isNotPolicy>(args...);
579578
return std::apply(
@@ -582,12 +581,6 @@ class EMBIND_VISIBILITY_DEFAULT val {
582581
},
583582
filteredArgs
584583
);
585-
#else
586-
// When std::apply is not available allow pointers by default. std::apply
587-
// could be polyfilled, but it requires a lot of code.
588-
static_assert(internal::conjunction<internal::isNotPolicy<Args>...>::value, "Using pointer policies with val requires C++17 or newer.");
589-
return internalCallWithPolicy<Kind, WithPolicies<allow_raw_pointers>, Ret>(handle, methodName, std::forward<decltype(args)>(args)...);
590-
#endif
591584
}
592585

593586
template<internal::EM_INVOKER_KIND Kind, typename Policy, typename Ret, typename... Args>

system/include/emscripten/wire.h

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,8 @@
77

88
#pragma once
99

10-
#if __cplusplus < 201103L
11-
#error "embind requires -std=c++11 or newer"
12-
#endif
13-
1410
#if __cplusplus < 201703L
15-
#warning "embind is likely moving to c++17 (https://github.com/emscripten-core/emscripten/issues/24850)"
11+
#error "embind requires -std=c++17 or newer"
1612
#endif
1713

1814
// A value moving between JavaScript and C++ has three representations:
@@ -579,27 +575,6 @@ struct reference : public allow_raw_pointers {};
579575

580576
namespace internal {
581577

582-
#if __cplusplus >= 201703L
583-
template <typename... Args> using conjunction = std::conjunction<Args...>;
584-
template <typename... Args> using disjunction = std::disjunction<Args...>;
585-
#else
586-
// Helper available in C++14.
587-
template <bool _Test, class _T1, class _T2>
588-
using conditional_t = typename std::conditional<_Test, _T1, _T2>::type;
589-
590-
template<class...> struct conjunction : std::true_type {};
591-
template<class B1> struct conjunction<B1> : B1 {};
592-
template<class B1, class... Bn>
593-
struct conjunction<B1, Bn...>
594-
: conditional_t<bool(B1::value), conjunction<Bn...>, B1> {};
595-
596-
template<class...> struct disjunction : std::false_type {};
597-
template<class B1> struct disjunction<B1> : B1 {};
598-
template<class B1, class... Bn>
599-
struct disjunction<B1, Bn...>
600-
: conditional_t<bool(B1::value), disjunction<Bn...>, B1> {};
601-
#endif
602-
603578
template<typename... Policies>
604579
struct isPolicy;
605580

@@ -674,10 +649,10 @@ struct GetReturnValuePolicy<ReturnType, T, Rest...> {
674649
};
675650

676651
template<typename... Policies>
677-
using isAsync = disjunction<std::is_same<async, Policies>...>;
652+
using isAsync = std::disjunction<std::is_same<async, Policies>...>;
678653

679654
template<typename... Policies>
680-
using isNonnullReturn = disjunction<std::is_same<nonnull<ret_val>, Policies>...>;
655+
using isNonnullReturn = std::disjunction<std::is_same<nonnull<ret_val>, Policies>...>;
681656

682657
// Build a tuple type that contains all the types where the predicate is true.
683658
// e.g. FilterTypes<std::is_integral, int, char, float> would return std::tuple<int, char>.
@@ -692,7 +667,6 @@ using FilterTypes = decltype(std::tuple_cat(
692667
>()...
693668
));
694669

695-
#if __cplusplus >= 201402L
696670
// Build a tuple that contains all the args where the predicate is true.
697671
template<template <class> class Predicate, typename... Args>
698672
auto Filter(Args&&... args) {
@@ -705,7 +679,6 @@ auto Filter(Args&&... args) {
705679
)(std::forward<Args>(args))...
706680
);
707681
}
708-
#endif
709682

710683
} // namespace internal
711684

test/cmake/cmake_with_emval/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16)
22

33
project(cmake_with_emval)
44

5-
set(CMAKE_CXX_STANDARD 11)
5+
set(CMAKE_CXX_STANDARD 17)
66
set(CMAKE_CXX_STANDARD_REQUIRED ON)
77

88
if (NO_GNU_EXTENSIONS)

test/test_other.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -991,8 +991,8 @@ def test_cmake_explicit_generator(self):
991991
self.run_process(cmd)
992992
self.assertExists(self.get_dir() + '/build.ninja')
993993

994-
# Tests that it's possible to pass C++11 or GNU++11 build modes to CMake by building code that
995-
# needs C++11 (embind)
994+
# Tests that it's possible to pass C++17 or GNU++17 build modes to CMake by building code that
995+
# needs C++17 (embind)
996996
@requires_ninja
997997
@parameterized({
998998
'': [[]],
@@ -1007,9 +1007,9 @@ def test_cmake_with_embind_cpp11_mode(self, args):
10071007

10081008
out = self.run_js('cmake_with_emval.js')
10091009
if '-DNO_GNU_EXTENSIONS=1' in args:
1010-
self.assertContained('Hello! __STRICT_ANSI__: 1, __cplusplus: 201103', out)
1010+
self.assertContained('Hello! __STRICT_ANSI__: 1, __cplusplus: 201703', out)
10111011
else:
1012-
self.assertContained('Hello! __STRICT_ANSI__: 0, __cplusplus: 201103', out)
1012+
self.assertContained('Hello! __STRICT_ANSI__: 0, __cplusplus: 201703', out)
10131013

10141014
# Tests that the Emscripten CMake toolchain option
10151015
def test_cmake_bitcode_static_libraries(self):
@@ -3395,10 +3395,10 @@ def test_embind_no_raw_pointers(self, filename):
33953395
'2gb': ['-sINITIAL_MEMORY=2200mb', '-sGLOBAL_BASE=2gb'],
33963396
})
33973397
@parameterized({
3398-
# With no arguments we are effectively testing c++17 since it is the default.
3398+
# With no arguments we are testing the default C++ version provided by clang.
33993399
'': [],
3400-
# Ensure embind compiles under C++11 which is the miniumum supported version.
3401-
'cxx11': ['-std=c++11', '-Wno-#warnings'],
3400+
# Ensure embind compiles under C++17 which is the miniumum supported version.
3401+
'cxx17': ['-std=c++17', '-Wno-#warnings'],
34023402
'o1': ['-O1'],
34033403
'o2': ['-O2'],
34043404
'o2_mem_growth': ['-O2', '-sALLOW_MEMORY_GROWTH', test_file('embind/isMemoryGrowthEnabled=true.cpp')],
@@ -3447,12 +3447,8 @@ def test_embind(self, *extra_args):
34473447
output = self.run_js(js_file)
34483448
self.assertNotContained('FAIL', output)
34493449

3450-
def test_embind_cxx11_warning(self):
3451-
err = self.run_process([EMXX, '-c', '-std=c++11', test_file('embind/test_unsigned.cpp')], stderr=PIPE).stderr
3452-
self.assertContained('#warning "embind is likely moving to c++17', err)
3453-
3454-
def test_embind_cxx03(self):
3455-
self.assert_fail([EMXX, '-c', '-std=c++03', test_file('embind/test_unsigned.cpp')], '#error "embind requires -std=c++11 or newer"')
3450+
def test_embind_cxx11(self):
3451+
self.assert_fail([EMXX, '-c', '-std=c++11', test_file('embind/test_unsigned.cpp')], '#error "embind requires -std=c++17 or newer"')
34563452

34573453
@requires_node
34583454
def test_embind_finalization(self):

0 commit comments

Comments
 (0)