Commit d767d0b5 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Use invoke_result v.s. std::result_of

Summary: [Folly] Use `invoke_result` v.s. `std::result_of`, which is deprecated.

Reviewed By: aary, ericniebler

Differential Revision: D7771480

fbshipit-source-id: 1d89d489be98bfbe2014f2d6b5fc037f02147876
parent 510f0b03
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <folly/Preprocessor.h> // for FB_ANONYMOUS_VARIABLE #include <folly/Preprocessor.h> // for FB_ANONYMOUS_VARIABLE
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
#include <folly/Traits.h> #include <folly/Traits.h>
#include <folly/functional/Invoke.h>
#include <folly/portability/GFlags.h> #include <folly/portability/GFlags.h>
#include <cassert> #include <cassert>
...@@ -125,7 +126,7 @@ struct BenchmarkSuspender { ...@@ -125,7 +126,7 @@ struct BenchmarkSuspender {
} }
template <class F> template <class F>
auto dismissing(F f) -> typename std::result_of<F()>::type { auto dismissing(F f) -> invoke_result_t<F> {
SCOPE_EXIT { rehire(); }; SCOPE_EXIT { rehire(); };
dismiss(); dismiss();
return f(); return f();
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <utility> #include <utility>
#include <folly/Optional.h> #include <folly/Optional.h>
#include <folly/functional/Invoke.h>
namespace folly { namespace folly {
...@@ -88,7 +89,7 @@ namespace detail { ...@@ -88,7 +89,7 @@ namespace detail {
template <class Func> template <class Func>
struct Lazy { struct Lazy {
typedef typename std::result_of<Func()>::type result_type; typedef invoke_result_t<Func> result_type;
static_assert( static_assert(
!std::is_const<Func>::value, !std::is_const<Func>::value,
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/Traits.h> #include <folly/Traits.h>
#include <folly/functional/Invoke.h>
#if FOLLY_HAVE_EXTRANDOM_SFMT19937 #if FOLLY_HAVE_EXTRANDOM_SFMT19937
#include <ext/random> #include <ext/random>
...@@ -65,9 +66,8 @@ class ThreadLocalPRNG { ...@@ -65,9 +66,8 @@ class ThreadLocalPRNG {
class Random { class Random {
private: private:
template <class RNG> template <class RNG>
using ValidRNG = typename std::enable_if< using ValidRNG = typename std::
std::is_unsigned<typename std::result_of<RNG&()>::type>::value, enable_if<std::is_unsigned<invoke_result_t<RNG&>>::value, RNG>::type;
RNG>::type;
template <class T> template <class T>
class SecureRNG { class SecureRNG {
......
...@@ -143,10 +143,10 @@ void Try<void>::throwIfFailed() const { ...@@ -143,10 +143,10 @@ void Try<void>::throwIfFailed() const {
template <typename F> template <typename F>
typename std::enable_if< typename std::enable_if<
!std::is_same<typename std::result_of<F()>::type, void>::value, !std::is_same<invoke_result_t<F>, void>::value,
Try<typename std::result_of<F()>::type>>::type Try<invoke_result_t<F>>>::type
makeTryWith(F&& f) { makeTryWith(F&& f) {
typedef typename std::result_of<F()>::type ResultType; using ResultType = invoke_result_t<F>;
try { try {
return Try<ResultType>(f()); return Try<ResultType>(f());
} catch (std::exception& e) { } catch (std::exception& e) {
...@@ -157,10 +157,9 @@ makeTryWith(F&& f) { ...@@ -157,10 +157,9 @@ makeTryWith(F&& f) {
} }
template <typename F> template <typename F>
typename std::enable_if< typename std::
std::is_same<typename std::result_of<F()>::type, void>::value, enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type
Try<void>>::type makeTryWith(F&& f) {
makeTryWith(F&& f) {
try { try {
f(); f();
return Try<void>(); return Try<void>();
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/Unit.h> #include <folly/Unit.h>
#include <folly/Utility.h> #include <folly/Utility.h>
#include <folly/functional/Invoke.h>
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
#include <type_traits> #include <type_traits>
...@@ -527,8 +528,8 @@ class Try<void> { ...@@ -527,8 +528,8 @@ class Try<void> {
*/ */
template <typename F> template <typename F>
typename std::enable_if< typename std::enable_if<
!std::is_same<typename std::result_of<F()>::type, void>::value, !std::is_same<invoke_result_t<F>, void>::value,
Try<typename std::result_of<F()>::type>>::type Try<invoke_result_t<F>>>::type
makeTryWith(F&& f); makeTryWith(F&& f);
/* /*
...@@ -539,10 +540,9 @@ makeTryWith(F&& f); ...@@ -539,10 +540,9 @@ makeTryWith(F&& f);
* @returns Try<void> holding the result of f * @returns Try<void> holding the result of f
*/ */
template <typename F> template <typename F>
typename std::enable_if< typename std::
std::is_same<typename std::result_of<F()>::type, void>::value, enable_if<std::is_same<invoke_result_t<F>, void>::value, Try<void>>::type
Try<void>>::type makeTryWith(F&& f);
makeTryWith(F&& f);
/** /**
* Tuple<Try<Type>...> -> std::tuple<Type...> * Tuple<Try<Type>...> -> std::tuple<Type...>
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <folly/functional/Invoke.h>
namespace folly { namespace folly {
namespace dptr_detail { namespace dptr_detail {
...@@ -82,14 +84,14 @@ struct SameType<T, Types...> { ...@@ -82,14 +84,14 @@ struct SameType<T, Types...> {
// to type T. // to type T.
template <typename V, typename T> template <typename V, typename T>
struct VisitorResult1 { struct VisitorResult1 {
typedef typename std::result_of<V (T*)>::type type; typedef invoke_result_t<V, T*> type;
}; };
// Determine the result type of applying a visitor of type V on a const pointer // Determine the result type of applying a visitor of type V on a const pointer
// to type T. // to type T.
template <typename V, typename T> template <typename V, typename T>
struct ConstVisitorResult1 { struct ConstVisitorResult1 {
typedef typename std::result_of<V (const T*)>::type type; typedef invoke_result_t<V, const T*> type;
}; };
// Determine the result type of applying a visitor of type V on pointers of // Determine the result type of applying a visitor of type V on pointers of
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
#pragma once #pragma once
#include <folly/functional/Invoke.h>
#include <folly/futures/Future.h> #include <folly/futures/Future.h>
namespace folly { namespace folly {
...@@ -37,10 +39,10 @@ class FutureExecutor : public ExecutorImpl { ...@@ -37,10 +39,10 @@ class FutureExecutor : public ExecutorImpl {
*/ */
template <typename F> template <typename F>
typename std::enable_if< typename std::enable_if<
folly::isFuture<typename std::result_of<F()>::type>::value, folly::isFuture<invoke_result_t<F>>::value,
typename std::result_of<F()>::type>::type invoke_result_t<F>>::type
addFuture(F func) { addFuture(F func) {
typedef typename std::result_of<F()>::type::value_type T; using T = typename invoke_result_t<F>::value_type;
folly::Promise<T> promise; folly::Promise<T> promise;
auto future = promise.getFuture(); auto future = promise.getFuture();
ExecutorImpl::add( ExecutorImpl::add(
...@@ -61,12 +63,10 @@ class FutureExecutor : public ExecutorImpl { ...@@ -61,12 +63,10 @@ class FutureExecutor : public ExecutorImpl {
*/ */
template <typename F> template <typename F>
typename std::enable_if< typename std::enable_if<
!folly::isFuture<typename std::result_of<F()>::type>::value, !folly::isFuture<invoke_result_t<F>>::value,
folly::Future<typename folly::Unit::Lift< folly::Future<typename folly::Unit::Lift<invoke_result_t<F>>::type>>::type
typename std::result_of<F()>::type>::type>>::type
addFuture(F func) { addFuture(F func) {
using T = using T = typename folly::Unit::Lift<invoke_result_t<F>>::type;
typename folly::Unit::Lift<typename std::result_of<F()>::type>::type;
folly::Promise<T> promise; folly::Promise<T> promise;
auto future = promise.getFuture(); auto future = promise.getFuture();
ExecutorImpl::add( ExecutorImpl::add(
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
#pragma once #pragma once
#include <folly/functional/Invoke.h>
namespace folly { namespace folly {
namespace observer_detail { namespace observer_detail {
...@@ -35,11 +37,10 @@ struct UnwrapSharedPtr<std::shared_ptr<T>> { ...@@ -35,11 +37,10 @@ struct UnwrapSharedPtr<std::shared_ptr<T>> {
}; };
template <typename F> template <typename F>
using ResultOf = using ResultOf = typename NonSharedPtr<invoke_result_t<F>>::type;
typename NonSharedPtr<typename std::result_of<F()>::type>::type;
template <typename F> template <typename F>
using ResultOfUnwrapSharedPtr = using ResultOfUnwrapSharedPtr =
typename UnwrapSharedPtr<typename std::result_of<F()>::type>::type; typename UnwrapSharedPtr<invoke_result_t<F>>::type;
} // namespace observer_detail } // namespace observer_detail
} // namespace folly } // namespace folly
...@@ -88,7 +88,7 @@ template <typename T> ...@@ -88,7 +88,7 @@ template <typename T>
template <typename F> template <typename F>
void TaskIterator<T>::addTask(F&& func) { void TaskIterator<T>::addTask(F&& func) {
static_assert( static_assert(
std::is_convertible<typename std::result_of<F()>::type, T>::value, std::is_convertible<invoke_result_t<F>, T>::value,
"TaskIterator<T>: T must be convertible from func()'s return type"); "TaskIterator<T>: T must be convertible from func()'s return type");
auto taskId = context_->totalTasks++; auto taskId = context_->totalTasks++;
...@@ -109,11 +109,11 @@ void TaskIterator<T>::addTask(F&& func) { ...@@ -109,11 +109,11 @@ void TaskIterator<T>::addTask(F&& func) {
} }
template <class InputIterator> template <class InputIterator>
TaskIterator<typename std::result_of< TaskIterator<
typename std::iterator_traits<InputIterator>::value_type()>::type> invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>>
addTasks(InputIterator first, InputIterator last) { addTasks(InputIterator first, InputIterator last) {
typedef typename std::result_of< typedef invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type typename std::iterator_traits<InputIterator>::value_type>
ResultType; ResultType;
typedef TaskIterator<ResultType> IteratorType; typedef TaskIterator<ResultType> IteratorType;
......
...@@ -41,9 +41,9 @@ class TaskIterator; ...@@ -41,9 +41,9 @@ class TaskIterator;
* @return movable, non-copyable iterator * @return movable, non-copyable iterator
*/ */
template <class InputIterator> template <class InputIterator>
TaskIterator<typename std::result_of< TaskIterator<invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>:: typename std::iterator_traits<InputIterator>::
type> inline addTasks(InputIterator first, InputIterator last); value_type>> inline addTasks(InputIterator first, InputIterator last);
template <typename T> template <typename T>
class TaskIterator { class TaskIterator {
...@@ -110,8 +110,8 @@ class TaskIterator { ...@@ -110,8 +110,8 @@ class TaskIterator {
private: private:
template <class InputIterator> template <class InputIterator>
friend TaskIterator<typename std::result_of< friend TaskIterator<
typename std::iterator_traits<InputIterator>::value_type()>::type> invoke_result_t<typename std::iterator_traits<InputIterator>::value_type>>
addTasks(InputIterator first, InputIterator last); addTasks(InputIterator first, InputIterator last);
struct Context { struct Context {
......
...@@ -15,15 +15,16 @@ ...@@ -15,15 +15,16 @@
*/ */
#pragma once #pragma once
#include <folly/functional/Invoke.h>
#include <folly/futures/Promise.h> #include <folly/futures/Promise.h>
namespace folly { namespace folly {
namespace fibers { namespace fibers {
template <typename F> template <typename F>
auto FiberManager::addTaskFuture(F&& func) -> folly::Future< auto FiberManager::addTaskFuture(F&& func)
typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> { -> folly::Future<typename folly::Unit::Lift<invoke_result_t<F>>::type> {
using T = typename std::result_of<F()>::type; using T = invoke_result_t<F>;
using FutureT = typename folly::Unit::Lift<T>::type; using FutureT = typename folly::Unit::Lift<T>::type;
folly::Promise<FutureT> p; folly::Promise<FutureT> p;
...@@ -37,11 +38,9 @@ auto FiberManager::addTaskFuture(F&& func) -> folly::Future< ...@@ -37,11 +38,9 @@ auto FiberManager::addTaskFuture(F&& func) -> folly::Future<
} }
template <typename F> template <typename F>
auto FiberManager::addTaskRemoteFuture(F&& func) -> folly::Future< auto FiberManager::addTaskRemoteFuture(F&& func)
typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> { -> folly::Future<typename folly::Unit::Lift<invoke_result_t<F>>::type> {
folly::Promise< folly::Promise<typename folly::Unit::Lift<invoke_result_t<F>>::type> p;
typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>
p;
auto f = p.getFuture(); auto f = p.getFuture();
addTaskRemote( addTaskRemote(
[ p = std::move(p), func = std::forward<F>(func), this ]() mutable { [ p = std::move(p), func = std::forward<F>(func), this ]() mutable {
......
...@@ -355,7 +355,7 @@ template <typename F, typename G> ...@@ -355,7 +355,7 @@ template <typename F, typename G>
struct FiberManager::AddTaskFinallyHelper { struct FiberManager::AddTaskFinallyHelper {
class Func; class Func;
typedef typename std::result_of<F()>::type Result; typedef invoke_result_t<F> Result;
class Finally { class Finally {
public: public:
...@@ -411,7 +411,7 @@ struct FiberManager::AddTaskFinallyHelper { ...@@ -411,7 +411,7 @@ struct FiberManager::AddTaskFinallyHelper {
template <typename F, typename G> template <typename F, typename G>
void FiberManager::addTaskFinally(F&& func, G&& finally) { void FiberManager::addTaskFinally(F&& func, G&& finally) {
typedef typename std::result_of<F()>::type Result; typedef invoke_result_t<F> Result;
static_assert( static_assert(
IsRvalueRefTry<typename FirstArgOf<G>::type>::value, IsRvalueRefTry<typename FirstArgOf<G>::type>::value,
...@@ -458,12 +458,12 @@ void FiberManager::addTaskFinally(F&& func, G&& finally) { ...@@ -458,12 +458,12 @@ void FiberManager::addTaskFinally(F&& func, G&& finally) {
} }
template <typename F> template <typename F>
typename std::result_of<F()>::type FiberManager::runInMainContext(F&& func) { invoke_result_t<F> FiberManager::runInMainContext(F&& func) {
if (UNLIKELY(activeFiber_ == nullptr)) { if (UNLIKELY(activeFiber_ == nullptr)) {
return func(); return func();
} }
typedef typename std::result_of<F()>::type Result; typedef invoke_result_t<F> Result;
folly::Try<Result> result; folly::Try<Result> result;
auto f = [&func, &result]() mutable { auto f = [&func, &result]() mutable {
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <folly/IntrusiveList.h> #include <folly/IntrusiveList.h>
#include <folly/Likely.h> #include <folly/Likely.h>
#include <folly/Try.h> #include <folly/Try.h>
#include <folly/functional/Invoke.h>
#include <folly/io/async/Request.h> #include <folly/io/async/Request.h>
#include <folly/experimental/ExecutionObserver.h> #include <folly/experimental/ExecutionObserver.h>
...@@ -204,8 +205,8 @@ class FiberManager : public ::folly::Executor { ...@@ -204,8 +205,8 @@ class FiberManager : public ::folly::Executor {
* The object will be destroyed once task execution is complete. * The object will be destroyed once task execution is complete.
*/ */
template <typename F> template <typename F>
auto addTaskFuture(F&& func) -> folly::Future< auto addTaskFuture(F&& func)
typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>; -> folly::Future<typename folly::Unit::Lift<invoke_result_t<F>>::type>;
/** /**
* Add a new task to be executed. Safe to call from other threads. * Add a new task to be executed. Safe to call from other threads.
* *
...@@ -223,8 +224,8 @@ class FiberManager : public ::folly::Executor { ...@@ -223,8 +224,8 @@ class FiberManager : public ::folly::Executor {
* The object will be destroyed once task execution is complete. * The object will be destroyed once task execution is complete.
*/ */
template <typename F> template <typename F>
auto addTaskRemoteFuture(F&& func) -> folly::Future< auto addTaskRemoteFuture(F&& func)
typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>; -> folly::Future<typename folly::Unit::Lift<invoke_result_t<F>>::type>;
// Executor interface calls addTaskRemote // Executor interface calls addTaskRemote
void add(folly::Func f) override { void add(folly::Func f) override {
...@@ -251,7 +252,7 @@ class FiberManager : public ::folly::Executor { ...@@ -251,7 +252,7 @@ class FiberManager : public ::folly::Executor {
* @return value returned by func(). * @return value returned by func().
*/ */
template <typename F> template <typename F>
typename std::result_of<F()>::type runInMainContext(F&& func); invoke_result_t<F> runInMainContext(F&& func);
/** /**
* Returns a refference to a fiber-local context for given Fiber. Should be * Returns a refference to a fiber-local context for given Fiber. Should be
...@@ -556,7 +557,7 @@ typename FirstArgOf<F>::type::value_type inline await(F&& func); ...@@ -556,7 +557,7 @@ typename FirstArgOf<F>::type::value_type inline await(F&& func);
* @return value returned by func(). * @return value returned by func().
*/ */
template <typename F> template <typename F>
typename std::result_of<F()>::type inline runInMainContext(F&& func) { invoke_result_t<F> inline runInMainContext(F&& func) {
auto fm = FiberManager::getFiberManagerUnsafe(); auto fm = FiberManager::getFiberManagerUnsafe();
if (UNLIKELY(fm == nullptr)) { if (UNLIKELY(fm == nullptr)) {
return func(); return func();
......
...@@ -13,7 +13,9 @@ ...@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include <folly/fibers/FiberManagerInternal.h> #include <folly/fibers/FiberManagerInternal.h>
#include <folly/functional/Invoke.h>
namespace folly { namespace folly {
namespace fibers { namespace fibers {
...@@ -21,16 +23,14 @@ namespace fibers { ...@@ -21,16 +23,14 @@ namespace fibers {
namespace { namespace {
template <class F, class G> template <class F, class G>
typename std::enable_if< typename std::enable_if<!std::is_same<invoke_result_t<F>, void>::value, void>::
!std::is_same<typename std::result_of<F()>::type, void>::value, type inline callFuncs(F&& f, G&& g, size_t id) {
void>::type inline callFuncs(F&& f, G&& g, size_t id) {
g(id, f()); g(id, f());
} }
template <class F, class G> template <class F, class G>
typename std::enable_if< typename std::enable_if<std::is_same<invoke_result_t<F>, void>::value, void>::
std::is_same<typename std::result_of<F()>::type, void>::value, type inline callFuncs(F&& f, G&& g, size_t id) {
void>::type inline callFuncs(F&& f, G&& g, size_t id) {
f(); f();
g(id); g(id);
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <folly/Try.h> #include <folly/Try.h>
#include <folly/fibers/traits.h> #include <folly/fibers/traits.h>
#include <folly/functional/Invoke.h>
namespace folly { namespace folly {
namespace fibers { namespace fibers {
...@@ -89,13 +90,13 @@ class Promise { ...@@ -89,13 +90,13 @@ class Promise {
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
std::is_convertible<typename std::result_of<F()>::type, T>::value && std::is_convertible<invoke_result_t<F>, T>::value &&
!std::is_same<T, void>::value>::type !std::is_same<T, void>::value>::type
fulfilHelper(F&& func); fulfilHelper(F&& func);
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
std::is_same<typename std::result_of<F()>::type, void>::value && std::is_same<invoke_result_t<F>, void>::value &&
std::is_same<T, void>::value>::type std::is_same<T, void>::value>::type
fulfilHelper(F&& func); fulfilHelper(F&& func);
}; };
......
...@@ -24,16 +24,17 @@ namespace fibers { ...@@ -24,16 +24,17 @@ namespace fibers {
template <class InputIterator> template <class InputIterator>
typename std::vector<typename std::enable_if< typename std::vector<typename std::enable_if<
!std::is_same< !std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
typename std::pair< typename std::pair<
size_t, size_t,
typename std::result_of<typename std::iterator_traits< invoke_result_t<
InputIterator>::value_type()>::type>>::type> typename std::iterator_traits<InputIterator>::value_type>>>::type>
collectN(InputIterator first, InputIterator last, size_t n) { collectN(InputIterator first, InputIterator last, size_t n) {
typedef typename std::result_of< typedef invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type Result; typename std::iterator_traits<InputIterator>::value_type>
Result;
assert(n > 0); assert(n > 0);
assert(std::distance(first, last) >= 0); assert(std::distance(first, last) >= 0);
assert(n <= static_cast<size_t>(std::distance(first, last))); assert(n <= static_cast<size_t>(std::distance(first, last)));
...@@ -83,8 +84,8 @@ collectN(InputIterator first, InputIterator last, size_t n) { ...@@ -83,8 +84,8 @@ collectN(InputIterator first, InputIterator last, size_t n) {
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
std::vector<size_t>>::type std::vector<size_t>>::type
collectN(InputIterator first, InputIterator last, size_t n) { collectN(InputIterator first, InputIterator last, size_t n) {
...@@ -138,14 +139,15 @@ template <class InputIterator> ...@@ -138,14 +139,15 @@ template <class InputIterator>
typename std::vector< typename std::vector<
typename std::enable_if< typename std::enable_if<
!std::is_same< !std::is_same<
typename std::result_of<typename std::iterator_traits< invoke_result_t<
InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type>:: typename std::iterator_traits<InputIterator>::value_type>>::
type> inline collectAll(InputIterator first, InputIterator last) { type> inline collectAll(InputIterator first, InputIterator last) {
typedef typename std::result_of< typedef invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type Result; typename std::iterator_traits<InputIterator>::value_type>
Result;
size_t n = size_t(std::distance(first, last)); size_t n = size_t(std::distance(first, last));
std::vector<Result> results; std::vector<Result> results;
std::vector<size_t> order(n); std::vector<size_t> order(n);
...@@ -170,8 +172,8 @@ typename std::vector< ...@@ -170,8 +172,8 @@ typename std::vector<
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
void>::type inline collectAll(InputIterator first, InputIterator last) { void>::type inline collectAll(InputIterator first, InputIterator last) {
forEach(first, last, [](size_t /* id */) {}); forEach(first, last, [](size_t /* id */) {});
...@@ -180,13 +182,13 @@ typename std::enable_if< ...@@ -180,13 +182,13 @@ typename std::enable_if<
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
!std::is_same< !std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
typename std::pair< typename std::pair<
size_t, size_t,
typename std::result_of<typename std::iterator_traits< invoke_result_t<
InputIterator>::value_type()>::type>>:: typename std::iterator_traits<InputIterator>::value_type>>>::
type inline collectAny(InputIterator first, InputIterator last) { type inline collectAny(InputIterator first, InputIterator last) {
auto result = collectN(first, last, 1); auto result = collectN(first, last, 1);
assert(result.size() == 1); assert(result.size() == 1);
...@@ -196,8 +198,8 @@ typename std::enable_if< ...@@ -196,8 +198,8 @@ typename std::enable_if<
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
size_t>::type inline collectAny(InputIterator first, InputIterator last) { size_t>::type inline collectAny(InputIterator first, InputIterator last) {
auto result = collectN(first, last, 1); auto result = collectN(first, last, 1);
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <folly/functional/Invoke.h>
namespace folly { namespace folly {
namespace fibers { namespace fibers {
...@@ -40,13 +42,13 @@ template <class InputIterator> ...@@ -40,13 +42,13 @@ template <class InputIterator>
typename std::vector< typename std::vector<
typename std::enable_if< typename std::enable_if<
!std::is_same< !std::is_same<
typename std::result_of<typename std::iterator_traits< invoke_result_t<
InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
typename std::pair< typename std::pair<
size_t, size_t,
typename std::result_of<typename std::iterator_traits< invoke_result_t<
InputIterator>::value_type()>::type>>:: typename std::iterator_traits<InputIterator>::value_type>>>::
type> inline collectN(InputIterator first, InputIterator last, size_t n); type> inline collectN(InputIterator first, InputIterator last, size_t n);
/** /**
...@@ -61,8 +63,8 @@ typename std::vector< ...@@ -61,8 +63,8 @@ typename std::vector<
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
std::vector<size_t>>:: std::vector<size_t>>::
type inline collectN(InputIterator first, InputIterator last, size_t n); type inline collectN(InputIterator first, InputIterator last, size_t n);
...@@ -79,14 +81,15 @@ typename std::enable_if< ...@@ -79,14 +81,15 @@ typename std::enable_if<
* @return vector of values returned by tasks * @return vector of values returned by tasks
*/ */
template <class InputIterator> template <class InputIterator>
typename std::vector<typename std::enable_if< typename std::vector<
!std::is_same< typename std::enable_if<
typename std::result_of< !std::is_same<
typename std::iterator_traits<InputIterator>::value_type()>::type, invoke_result_t<
void>::value, typename std::iterator_traits<InputIterator>::value_type>,
typename std::result_of< void>::value,
typename std::iterator_traits<InputIterator>::value_type()>:: invoke_result_t<
type>::type> inline collectAll(InputIterator first, InputIterator last); typename std::iterator_traits<InputIterator>::value_type>>::
type> inline collectAll(InputIterator first, InputIterator last);
/** /**
* collectAll specialization for functions returning void * collectAll specialization for functions returning void
...@@ -97,8 +100,8 @@ typename std::vector<typename std::enable_if< ...@@ -97,8 +100,8 @@ typename std::vector<typename std::enable_if<
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
void>::type inline collectAll(InputIterator first, InputIterator last); void>::type inline collectAll(InputIterator first, InputIterator last);
...@@ -115,13 +118,13 @@ typename std::enable_if< ...@@ -115,13 +118,13 @@ typename std::enable_if<
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
!std::is_same< !std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
typename std::pair< typename std::pair<
size_t, size_t,
typename std::result_of<typename std::iterator_traits< invoke_result_t<
InputIterator>::value_type()>::type>>:: typename std::iterator_traits<InputIterator>::value_type>>>::
type inline collectAny(InputIterator first, InputIterator last); type inline collectAny(InputIterator first, InputIterator last);
/** /**
...@@ -135,8 +138,8 @@ typename std::enable_if< ...@@ -135,8 +138,8 @@ typename std::enable_if<
template <class InputIterator> template <class InputIterator>
typename std::enable_if< typename std::enable_if<
std::is_same< std::is_same<
typename std::result_of< invoke_result_t<
typename std::iterator_traits<InputIterator>::value_type()>::type, typename std::iterator_traits<InputIterator>::value_type>,
void>::value, void>::value,
size_t>::type inline collectAny(InputIterator first, InputIterator last); size_t>::type inline collectAny(InputIterator first, InputIterator last);
} // namespace fibers } // namespace fibers
......
...@@ -574,12 +574,10 @@ SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) { ...@@ -574,12 +574,10 @@ SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) {
// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T> // makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
template <class F> template <class F>
typename std::enable_if< typename std::
isSemiFuture<typename std::result_of<F()>::type>::value, enable_if<isSemiFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type
typename std::result_of<F()>::type>::type makeSemiFutureWith(F&& func) {
makeSemiFutureWith(F&& func) { using InnerType = typename isSemiFuture<invoke_result_t<F>>::Inner;
using InnerType =
typename isSemiFuture<typename std::result_of<F()>::type>::Inner;
try { try {
return std::forward<F>(func)(); return std::forward<F>(func)();
} catch (std::exception& e) { } catch (std::exception& e) {
...@@ -595,10 +593,10 @@ makeSemiFutureWith(F&& func) { ...@@ -595,10 +593,10 @@ makeSemiFutureWith(F&& func) {
// makeSemiFutureWith(void()) -> SemiFuture<Unit> // makeSemiFutureWith(void()) -> SemiFuture<Unit>
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!(isSemiFuture<typename std::result_of<F()>::type>::value), !(isSemiFuture<invoke_result_t<F>>::value),
SemiFuture<Unit::LiftT<typename std::result_of<F()>::type>>>::type SemiFuture<Unit::LiftT<invoke_result_t<F>>>>::type
makeSemiFutureWith(F&& func) { makeSemiFutureWith(F&& func) {
using LiftedResult = Unit::LiftT<typename std::result_of<F()>::type>; using LiftedResult = Unit::LiftT<invoke_result_t<F>>;
return makeSemiFuture<LiftedResult>( return makeSemiFuture<LiftedResult>(
makeTryWith([&func]() mutable { return std::forward<F>(func)(); })); makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
} }
...@@ -1118,11 +1116,10 @@ inline Future<Unit> makeFuture() { ...@@ -1118,11 +1116,10 @@ inline Future<Unit> makeFuture() {
// makeFutureWith(Future<T>()) -> Future<T> // makeFutureWith(Future<T>()) -> Future<T>
template <class F> template <class F>
typename std::enable_if<isFuture<typename std::result_of<F()>::type>::value, typename std::
typename std::result_of<F()>::type>::type enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type
makeFutureWith(F&& func) { makeFutureWith(F&& func) {
using InnerType = using InnerType = typename isFuture<invoke_result_t<F>>::Inner;
typename isFuture<typename std::result_of<F()>::type>::Inner;
try { try {
return std::forward<F>(func)(); return std::forward<F>(func)();
} catch (std::exception& e) { } catch (std::exception& e) {
...@@ -1137,10 +1134,10 @@ makeFutureWith(F&& func) { ...@@ -1137,10 +1134,10 @@ makeFutureWith(F&& func) {
// makeFutureWith(void()) -> Future<Unit> // makeFutureWith(void()) -> Future<Unit>
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!(isFuture<typename std::result_of<F()>::type>::value), !(isFuture<invoke_result_t<F>>::value),
Future<Unit::LiftT<typename std::result_of<F()>::type>>>::type Future<Unit::LiftT<invoke_result_t<F>>>>::type
makeFutureWith(F&& func) { makeFutureWith(F&& func) {
using LiftedResult = Unit::LiftT<typename std::result_of<F()>::type>; using LiftedResult = Unit::LiftT<invoke_result_t<F>>;
return makeFuture<LiftedResult>( return makeFuture<LiftedResult>(
makeTryWith([&func]() mutable { return std::forward<F>(func)(); })); makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#pragma once #pragma once
#include <folly/Random.h> #include <folly/Random.h>
#include <folly/functional/Invoke.h>
#include <folly/futures/Future.h> #include <folly/futures/Future.h>
namespace folly { namespace folly {
...@@ -59,7 +60,7 @@ namespace futures { ...@@ -59,7 +60,7 @@ namespace futures {
* overflow due to the recursive nature of the retry implementation * overflow due to the recursive nature of the retry implementation
*/ */
template <class Policy, class FF> template <class Policy, class FF>
typename std::result_of<FF(size_t)>::type retrying(Policy&& p, FF&& ff); invoke_result_t<FF, size_t> retrying(Policy&& p, FF&& ff);
namespace detail { namespace detail {
...@@ -68,7 +69,7 @@ struct retrying_policy_fut_tag {}; ...@@ -68,7 +69,7 @@ struct retrying_policy_fut_tag {};
template <class Policy> template <class Policy>
struct retrying_policy_traits { struct retrying_policy_traits {
using result = std::result_of_t<Policy(size_t, const exception_wrapper&)>; using result = invoke_result_t<Policy, size_t, const exception_wrapper&>;
using is_raw = std::is_same<result, bool>; using is_raw = std::is_same<result, bool>;
using is_fut = std::is_same<result, Future<bool>>; using is_fut = std::is_same<result, Future<bool>>;
using tag = typename std::conditional< using tag = typename std::conditional<
...@@ -80,7 +81,7 @@ struct retrying_policy_traits { ...@@ -80,7 +81,7 @@ struct retrying_policy_traits {
template <class Policy, class FF, class Prom> template <class Policy, class FF, class Prom>
void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) { void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) {
using F = typename std::result_of<FF(size_t)>::type; using F = invoke_result_t<FF, size_t>;
using T = typename F::value_type; using T = typename F::value_type;
auto f = makeFutureWith([&] { return ff(k++); }); auto f = makeFutureWith([&] { return ff(k++); });
f.then([k, f.then([k,
...@@ -110,9 +111,8 @@ void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) { ...@@ -110,9 +111,8 @@ void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) {
} }
template <class Policy, class FF> template <class Policy, class FF>
typename std::result_of<FF(size_t)>::type invoke_result_t<FF, size_t> retrying(size_t k, Policy&& p, FF&& ff) {
retrying(size_t k, Policy&& p, FF&& ff) { using F = invoke_result_t<FF, size_t>;
using F = typename std::result_of<FF(size_t)>::type;
using T = typename F::value_type; using T = typename F::value_type;
auto prom = Promise<T>(); auto prom = Promise<T>();
auto f = prom.getFuture(); auto f = prom.getFuture();
...@@ -122,7 +122,7 @@ retrying(size_t k, Policy&& p, FF&& ff) { ...@@ -122,7 +122,7 @@ retrying(size_t k, Policy&& p, FF&& ff) {
} }
template <class Policy, class FF> template <class Policy, class FF>
typename std::result_of<FF(size_t)>::type invoke_result_t<FF, size_t>
retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) { retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) {
auto q = [pm = std::forward<Policy>(p)](size_t k, exception_wrapper x) { auto q = [pm = std::forward<Policy>(p)](size_t k, exception_wrapper x) {
return makeFuture<bool>(pm(k, x)); return makeFuture<bool>(pm(k, x));
...@@ -131,7 +131,7 @@ retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) { ...@@ -131,7 +131,7 @@ retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) {
} }
template <class Policy, class FF> template <class Policy, class FF>
typename std::result_of<FF(size_t)>::type invoke_result_t<FF, size_t>
retrying(Policy&& p, FF&& ff, retrying_policy_fut_tag) { retrying(Policy&& p, FF&& ff, retrying_policy_fut_tag) {
return retrying(0, std::forward<Policy>(p), std::forward<FF>(ff)); return retrying(0, std::forward<Policy>(p), std::forward<FF>(ff));
} }
...@@ -231,7 +231,7 @@ retryingPolicyCappedJitteredExponentialBackoff( ...@@ -231,7 +231,7 @@ retryingPolicyCappedJitteredExponentialBackoff(
} // namespace detail } // namespace detail
template <class Policy, class FF> template <class Policy, class FF>
typename std::result_of<FF(size_t)>::type retrying(Policy&& p, FF&& ff) { invoke_result_t<FF, size_t> retrying(Policy&& p, FF&& ff) {
using tag = typename detail::retrying_policy_traits<Policy>::tag; using tag = typename detail::retrying_policy_traits<Policy>::tag;
return detail::retrying(std::forward<Policy>(p), std::forward<FF>(ff), tag()); return detail::retrying(std::forward<Policy>(p), std::forward<FF>(ff), tag());
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/Try.h> #include <folly/Try.h>
#include <folly/functional/Invoke.h>
#include <folly/futures/Future.h> #include <folly/futures/Future.h>
#include <folly/futures/Promise.h> #include <folly/futures/Promise.h>
...@@ -142,16 +143,16 @@ SemiFuture<Unit> makeSemiFuture(); ...@@ -142,16 +143,16 @@ SemiFuture<Unit> makeSemiFuture();
// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T> // makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
template <class F> template <class F>
typename std::enable_if<isSemiFuture<typename std::result_of<F()>::type>::value, typename std::
typename std::result_of<F()>::type>::type enable_if<isSemiFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type
makeSemiFutureWith(F&& func); makeSemiFutureWith(F&& func);
// makeSemiFutureWith(T()) -> SemiFuture<T> // makeSemiFutureWith(T()) -> SemiFuture<T>
// makeSemiFutureWith(void()) -> SemiFuture<Unit> // makeSemiFutureWith(void()) -> SemiFuture<Unit>
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!(isSemiFuture<typename std::result_of<F()>::type>::value), !(isSemiFuture<invoke_result_t<F>>::value),
SemiFuture<typename Unit::Lift<typename std::result_of<F()>::type>::type>>::type SemiFuture<typename Unit::Lift<invoke_result_t<F>>::type>>::type
makeSemiFutureWith(F&& func); makeSemiFutureWith(F&& func);
/// Make a failed Future from an exception_ptr. /// Make a failed Future from an exception_ptr.
...@@ -226,16 +227,16 @@ Future<Unit> makeFuture(); ...@@ -226,16 +227,16 @@ Future<Unit> makeFuture();
// makeFutureWith(Future<T>()) -> Future<T> // makeFutureWith(Future<T>()) -> Future<T>
template <class F> template <class F>
typename std::enable_if<isFuture<typename std::result_of<F()>::type>::value, typename std::
typename std::result_of<F()>::type>::type enable_if<isFuture<invoke_result_t<F>>::value, invoke_result_t<F>>::type
makeFutureWith(F&& func); makeFutureWith(F&& func);
// makeFutureWith(T()) -> Future<T> // makeFutureWith(T()) -> Future<T>
// makeFutureWith(void()) -> Future<Unit> // makeFutureWith(void()) -> Future<Unit>
template <class F> template <class F>
typename std::enable_if< typename std::enable_if<
!(isFuture<typename std::result_of<F()>::type>::value), !(isFuture<invoke_result_t<F>>::value),
Future<typename Unit::Lift<typename std::result_of<F()>::type>::type>>::type Future<typename Unit::Lift<invoke_result_t<F>>::type>>::type
makeFutureWith(F&& func); makeFutureWith(F&& func);
/// Make a failed Future from an exception_ptr. /// Make a failed Future from an exception_ptr.
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#endif #endif
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/functional/Invoke.h>
// Ignore shadowing warnings within this file, so includers can use -Wshadow. // Ignore shadowing warnings within this file, so includers can use -Wshadow.
FOLLY_PUSH_WARNING FOLLY_PUSH_WARNING
...@@ -484,8 +485,8 @@ class Map : public Operator<Map<Predicate>> { ...@@ -484,8 +485,8 @@ class Map : public Operator<Map<Predicate>> {
template < template <
class Value, class Value,
class Source, class Source,
class Result = typename ArgumentReference< class Result =
typename std::result_of<Predicate(Value)>::type>::type> typename ArgumentReference<invoke_result_t<Predicate, Value>>::type>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> { class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_; Source source_;
Predicate pred_; Predicate pred_;
...@@ -1017,10 +1018,10 @@ class Order : public Operator<Order<Selector, Comparer>> { ...@@ -1017,10 +1018,10 @@ class Order : public Operator<Order<Selector, Comparer>> {
class Value, class Value,
class Source, class Source,
class StorageType = typename std::decay<Value>::type, class StorageType = typename std::decay<Value>::type,
class Result = typename std::result_of<Selector(Value)>::type> class Result = invoke_result_t<Selector, Value>>
class Generator class Generator : public GenImpl<
: public GenImpl<StorageType&&, StorageType&&,
Generator<Value, Source, StorageType, Result>> { Generator<Value, Source, StorageType, Result>> {
static_assert(!Source::infinite, "Cannot sort infinite source!"); static_assert(!Source::infinite, "Cannot sort infinite source!");
Source source_; Source source_;
Selector selector_; Selector selector_;
...@@ -1119,7 +1120,7 @@ class GroupBy : public Operator<GroupBy<Selector>> { ...@@ -1119,7 +1120,7 @@ class GroupBy : public Operator<GroupBy<Selector>> {
class Value, class Value,
class Source, class Source,
class ValueDecayed = typename std::decay<Value>::type, class ValueDecayed = typename std::decay<Value>::type,
class Key = typename std::result_of<Selector(Value)>::type, class Key = invoke_result_t<Selector, Value>,
class KeyDecayed = typename std::decay<Key>::type> class KeyDecayed = typename std::decay<Key>::type>
class Generator class Generator
: public GenImpl< : public GenImpl<
...@@ -1227,7 +1228,7 @@ class Distinct : public Operator<Distinct<Selector>> { ...@@ -1227,7 +1228,7 @@ class Distinct : public Operator<Distinct<Selector>> {
// of a value to the downstream operators. // of a value to the downstream operators.
typedef const StorageType& ParamType; typedef const StorageType& ParamType;
typedef typename std::result_of<Selector(ParamType)>::type KeyType; typedef invoke_result_t<Selector, ParamType> KeyType;
typedef typename std::decay<KeyType>::type KeyStorageType; typedef typename std::decay<KeyType>::type KeyStorageType;
public: public:
...@@ -2091,8 +2092,7 @@ class Min : public Operator<Min<Selector, Comparer>> { ...@@ -2091,8 +2092,7 @@ class Min : public Operator<Min<Selector, Comparer>> {
class Value, class Value,
class Source, class Source,
class StorageType = typename std::decay<Value>::type, class StorageType = typename std::decay<Value>::type,
class Key = typename std::decay< class Key = typename std::decay<invoke_result_t<Selector, Value>>::type>
typename std::result_of<Selector(Value)>::type>::type>
Optional<StorageType> compose(const GenImpl<Value, Source>& source) const { Optional<StorageType> compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, static_assert(!Source::infinite,
"Calling min or max on an infinite source will cause " "Calling min or max on an infinite source will cause "
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <folly/MPMCPipeline.h> #include <folly/MPMCPipeline.h>
#include <folly/experimental/EventCount.h> #include <folly/experimental/EventCount.h>
#include <folly/functional/Invoke.h>
namespace folly { namespace folly {
namespace gen { namespace gen {
...@@ -57,10 +58,10 @@ class PMap : public Operator<PMap<Predicate>> { ...@@ -57,10 +58,10 @@ class PMap : public Operator<PMap<Predicate>> {
class Value, class Value,
class Source, class Source,
class Input = typename std::decay<Value>::type, class Input = typename std::decay<Value>::type,
class Output = typename std::decay< class Output =
typename std::result_of<Predicate(Value)>::type>::type> typename std::decay<invoke_result_t<Predicate, Value>>::type>
class Generator : class Generator
public GenImpl<Output, Generator<Value, Source, Input, Output>> { : public GenImpl<Output, Generator<Value, Source, Input, Output>> {
Source source_; Source source_;
Predicate pred_; Predicate pred_;
const size_t nThreads_; const size_t nThreads_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment