Skip to content

Commit

Permalink
Concepts support
Browse files Browse the repository at this point in the history
Add experimental support for C++ concepts.  Concepts are wrapped with
a GCC6_CONCEPT() macro to hide them when concept support is disabled.

A few methods in future<>, as well as some primitives in future-util.hg are
now constrained.

Note that concepts support is experimental and may change until the
Technical Specification is approved.

Message-Id: <20170213182317.29082-1-avi@scylladb.com>
  • Loading branch information
avikivity committed Feb 27, 2017
1 parent 3dd252e commit 78f1cf3
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 1 deletion.
6 changes: 6 additions & 0 deletions configure.py
Expand Up @@ -260,6 +260,8 @@ def sanitize_vptr_flag(compiler):
help = 'Link with boost statically')
add_tristate(arg_parser, name = 'hwloc', dest = 'hwloc', help = 'hwloc support')
add_tristate(arg_parser, name = 'xen', dest = 'xen', help = 'Xen support')
arg_parser.add_argument('--enable-gcc6-concepts', dest='gcc6_concepts', action='store_true', default=False,
help='enable experimental support for C++ Concepts as implemented in GCC 6')
args = arg_parser.parse_args()

libnet = [
Expand Down Expand Up @@ -377,6 +379,10 @@ def have_xen():
print("Error: only xen or dpdk can be used, not both.")
sys.exit(1)

if args.gcc6_concepts:
defines.append('HAVE_GCC6_CONCEPTS')
args.user_cflags += ' -fconcepts'

if args.staticcxx:
libs = libs.replace('-lstdc++', '')
libs += ' -static-libgcc -static-libstdc++'
Expand Down
47 changes: 47 additions & 0 deletions core/future-util.hh
Expand Up @@ -80,6 +80,7 @@ struct parallel_for_each_state {
/// complete. If one or more return an exception, the return value
/// contains one of the exceptions.
template <typename Iterator, typename Func>
GCC6_CONCEPT( requires requires (Func f, Iterator i) { { f(*i++) } -> future<>; } )
inline
future<>
parallel_for_each(Iterator begin, Iterator end, Func&& func) {
Expand Down Expand Up @@ -131,6 +132,7 @@ parallel_for_each(Iterator begin, Iterator end, Func&& func) {
/// \c func returned an exceptional future, then the return
/// value will contain one of those exceptions.
template <typename Range, typename Func>
GCC6_CONCEPT( requires requires (Func f, Range r) { { f(*r.begin()) } -> future<>; } )
inline
future<>
parallel_for_each(Range&& range, Func&& func) {
Expand Down Expand Up @@ -188,6 +190,7 @@ using stop_iteration = bool_class<stop_iteration_tag>;
/// \return a ready future if we stopped successfully, or a failed future if
/// a call to to \c action failed.
template<typename AsyncAction>
GCC6_CONCEPT( requires seastar::ApplyReturns<AsyncAction, stop_iteration> || seastar::ApplyReturns<AsyncAction, future<stop_iteration>> )
static inline
future<> repeat(AsyncAction&& action) {
using futurator = futurize<std::result_of_t<AsyncAction()>>;
Expand Down Expand Up @@ -259,6 +262,11 @@ using repeat_until_value_return_type
/// \return a ready future if we stopped successfully, or a failed future if
/// a call to to \c action failed. The \c optional's value is returned.
template<typename AsyncAction>
GCC6_CONCEPT( requires requires (AsyncAction aa) {
requires is_future<decltype(aa())>::value;
bool(aa().get0());
aa().get0().value();
} )
repeat_until_value_return_type<AsyncAction>
repeat_until_value(AsyncAction&& action) {
using type_helper = repeat_until_value_type_helper<std::result_of_t<AsyncAction()>>;
Expand Down Expand Up @@ -312,6 +320,7 @@ repeat_until_value(AsyncAction&& action) {
/// \return a ready future if we stopped successfully, or a failed future if
/// a call to to \c action failed.
template<typename AsyncAction, typename StopCondition>
GCC6_CONCEPT( requires seastar::ApplyReturns<StopCondition, bool> && seastar::ApplyReturns<AsyncAction, future<>> )
static inline
future<> do_until(StopCondition&& stop_cond, AsyncAction&& action) {
promise<> p;
Expand All @@ -329,6 +338,7 @@ future<> do_until(StopCondition&& stop_cond, AsyncAction&& action) {
/// that becomes ready when you wish it to be called again.
/// \return a future<> that will resolve to the first failure of \c action
template<typename AsyncAction>
GCC6_CONCEPT( requires seastar::ApplyReturns<AsyncAction, future<>> )
static inline
future<> keep_doing(AsyncAction&& action) {
return repeat([action = std::forward<AsyncAction>(action)] () mutable {
Expand All @@ -351,6 +361,7 @@ future<> keep_doing(AsyncAction&& action) {
/// \return a ready future on success, or the first failed future if
/// \c action failed.
template<typename Iterator, typename AsyncAction>
GCC6_CONCEPT( requires requires (Iterator i, AsyncAction aa) { { aa(*i) } -> future<> } )
static inline
future<> do_for_each(Iterator begin, Iterator end, AsyncAction&& action) {
if (begin == end) {
Expand Down Expand Up @@ -385,6 +396,7 @@ future<> do_for_each(Iterator begin, Iterator end, AsyncAction&& action) {
/// \return a ready future on success, or the first failed future if
/// \c action failed.
template<typename Container, typename AsyncAction>
GCC6_CONCEPT( requires requires (Container c, AsyncAction aa) { { aa(*c.begin()) } -> future<> } )
static inline
future<> do_for_each(Container& c, AsyncAction&& action) {
return do_for_each(std::begin(c), std::end(c), std::forward<AsyncAction>(action));
Expand Down Expand Up @@ -438,6 +450,39 @@ public:
}
/// \endcond

namespace seastar {

GCC6_CONCEPT(

/// \cond internal
namespace impl {


// Want: folds

template <typename T>
struct is_tuple_of_futures : std::false_type {
};

template <>
struct is_tuple_of_futures<std::tuple<>> : std::true_type {
};

template <typename... T, typename... Rest>
struct is_tuple_of_futures<std::tuple<future<T...>, Rest...>> : is_tuple_of_futures<std::tuple<Rest...>> {
};

}
/// \endcond

template <typename... Futs>
concept bool AllAreFutures = impl::is_tuple_of_futures<std::tuple<Futs...>>::value;

)

}


/// Wait for many futures to complete, capturing possible errors (variadic version).
///
/// Given a variable number of futures as input, wait for all of them
Expand All @@ -448,6 +493,7 @@ public:
/// \return an \c std::tuple<> of all the futures in the input; when
/// ready, all contained futures will be ready as well.
template <typename... Futs>
GCC6_CONCEPT( requires seastar::AllAreFutures<Futs...> )
inline
future<std::tuple<Futs...>>
when_all(Futs&&... futs) {
Expand Down Expand Up @@ -532,6 +578,7 @@ do_when_all(FutureIterator begin, FutureIterator end) {
/// \return an \c std::vector<> of all the futures in the input; when
/// ready, all contained futures will be ready as well.
template <typename FutureIterator>
GCC6_CONCEPT( requires requires (FutureIterator i) { { *i++ }; requires is_future<std::remove_reference_t<decltype(*i)>>::value; } )
inline
future<std::vector<typename std::iterator_traits<FutureIterator>::value_type>>
when_all(FutureIterator begin, FutureIterator end) {
Expand Down
31 changes: 30 additions & 1 deletion core/future.hh
Expand Up @@ -33,7 +33,7 @@
#include <assert.h>
#include <cstdlib>
#include "function_traits.hh"

#include "../util/gcc6-concepts.hh"

/// \defgroup future-module Futures and Promises
///
Expand Down Expand Up @@ -648,6 +648,27 @@ using futurize_t = typename futurize<T>::type;

/// @}

namespace seastar {

GCC6_CONCEPT(

template <typename T>
concept bool Future = is_future<T>::value;

template <typename Func, typename... T>
concept bool CanApply = requires (Func f, T... args) {
f(std::forward<T>(args)...);
};

template <typename Func, typename Return, typename... T>
concept bool ApplyReturns = requires (Func f, T... args) {
{ f(std::forward<T>(args)...) } -> Return;
};

)

}

/// \addtogroup future-module
/// @{

Expand Down Expand Up @@ -852,6 +873,7 @@ public:
/// \return a \c future representing the return value of \c func, applied
/// to the eventual value of this future.
template <typename Func, typename Result = futurize_t<std::result_of_t<Func(T&&...)>>>
GCC6_CONCEPT( requires seastar::CanApply<Func, T...> )
Result
then(Func&& func) noexcept {
using futurator = futurize<std::result_of_t<Func(T&&...)>>;
Expand Down Expand Up @@ -898,6 +920,7 @@ public:
/// \return a \c future representing the return value of \c func, applied
/// to the eventual value of this future.
template <typename Func, typename Result = futurize_t<std::result_of_t<Func(future)>>>
GCC6_CONCEPT( requires seastar::CanApply<Func, future> )
Result
then_wrapped(Func&& func) noexcept {
using futurator = futurize<std::result_of_t<Func(future)>>;
Expand Down Expand Up @@ -957,6 +980,7 @@ public:
* nested will be propagated.
*/
template <typename Func>
GCC6_CONCEPT( requires seastar::ApplyReturns<Func, void> || seastar::ApplyReturns<Func, future<>> )
future<T...> finally(Func&& func) noexcept {
return then_wrapped(finally_body<Func, is_future<std::result_of_t<Func()>>::value>(std::forward<Func>(func)));
}
Expand Down Expand Up @@ -1042,6 +1066,11 @@ public:
/// successful value; Because handle_exception() is used here on a
/// future<>, the handler function does not need to return anything.
template <typename Func>
/* Broken?
GCC6_CONCEPT( requires seastar::ApplyReturns<Func, future<T...>, std::exception_ptr>
|| (sizeof...(T) == 0 && seastar::ApplyReturns<Func, void, std::exception_ptr>)
|| (sizeof...(T) == 1 && seastar::ApplyReturns<Func, T..., std::exception_ptr>)
) */
future<T...> handle_exception(Func&& func) noexcept {
using func_ret = std::result_of_t<Func(std::exception_ptr)>;
return then_wrapped([func = std::forward<Func>(func)]
Expand Down
33 changes: 33 additions & 0 deletions util/gcc6-concepts.hh
@@ -0,0 +1,33 @@
/*
* This file is open source software, licensed to you under the terms
* of the Apache License, Version 2.0 (the "License"). See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Copyright (C) 2016 ScyllaDB
*/
#pragma once

#ifndef HAVE_GCC6_CONCEPTS

#define GCC6_CONCEPT(x...)
#define GCC6_NO_CONCEPT(x...) x

#else

#define GCC6_CONCEPT(x...) x
#define GCC6_NO_CONCEPT(x...)

#endif

0 comments on commit 78f1cf3

Please sign in to comment.