Maybe I don't know something about C++, but I found incomprehensible (and dangerous in my opinion) C++ compiler behavior.
MSVC, g++ and Clang behave the same.
Q: Why function ::a::f is visible as f inside b::f(bool)?
namespace a {
struct C {
bool value;
C(): value(false) {}
C(C const &): value(false) {}
explicit C(bool value): value(value) {}
};
inline C f(bool value) { return C(value); }
// why this function is visible as `f` inside `b::f(bool)`?
inline C f(C const &value) { return C(not value.value); }
}
namespace b {
inline bool f(::a::C const &value) { return value.value; }
inline bool f(bool value) {
#ifdef WORK_AROUND_PROBLEM
return b::f(a::f(value));
#else
// why `::a::f` is visible as `f` here?
return f(a::f(value)).value;
#endif
}
}
int main(int, char **) {
if (b::f(false) or (! b::f(true))) return 1;
if (b::f(0)) return 2;
if (b::f(a::C(false)) || (! b::f(a::C(true)))) return 3;
return 0;
}
======== UPDATE ========
First code example can be explained with Argument dependent lookup https://en.cppreference.com/w/cpp/language/adl. Thanks for the answers. =)
But it's still not clear to me how to properly deal with set of "universal functions". In context of first example I cannot figure out how to properly replace f (C const &) with template <class ... T> auto f (T && ...)
As next example [c++20], I've posted a piece of more real code (but simplified) here.
This is a part of header-only library. Here I would like to have a universal make function for the ::exception::Walker. And a universal make function for exception::Backtrace. Moreover, I would like to have minimal connectivity between ::exception::walker and ::exception::backtrace namespaces. See comments in the code below.
#include <cstdint>
#include <list>
#include <ranges>
#include <utility>
#include <iterator>
#include <iostream>
#include <algorithm>
#include <exception>
#include <stdexcept>
#include <type_traits>
// from <.../exception/walker.fwd.hpp>
namespace exception {
namespace walker {
struct Class;
struct Iterator;
using Value = ::std::exception_ptr;
template <class ... T> auto make(T && ...);
} // namespace walker
using Walker = walker::Class;
} // namespace exception
// from <.../exception/backtrace.fwd.hpp>
namespace exception {
namespace backtrace {
using Item = ::std::exception_ptr;
using Class = ::std::vector<Item>;
auto make();
template <class T> auto make(T &&source);
template <class beginT, class endT> auto make(beginT &&begin, endT &&end);
} // namespace backtrace
using BackTrace = backtrace::Class;
} // namespace exception
// from <.../exception/walker.hpp>
namespace exception::walker {
struct Class final {
using Value = walker::Value;
using Iterator = walker::Iterator;
auto begin() const noexcept(true);
auto end() const noexcept(true);
Class() noexcept(true) = default;
Class(Class &&) noexcept(true) = default;
Class(Class const &) noexcept(true) = default;
template <class headT, class ... tailT> requires((0 < sizeof ... (tailT)) or (not ::std::is_base_of_v<Class, ::std::decay_t<headT>>))
explicit Class(headT &&head, tailT && ... tail) noexcept(true);
private:
Value last_;
// There was a lot of overloads
template <class T> decltype(auto) make_value_(T &&value) noexcept(true);
};
struct Iterator final {
using iterator_category = ::std::input_iterator_tag;
using difference_type = ::std::ptrdiff_t;
using value_type = Value;
using pointer = Value *;
using reference = Value &;
inline auto & operator*() const noexcept(true) { return value_; }
inline auto * operator->() const noexcept(true) { return &value_; }
inline auto & operator++() noexcept(true) {
auto const temporary_ = value_;
if (static_cast<bool>(temporary_)) {
value_ = {};
try { ::std::rethrow_exception(temporary_); }
catch(::std::exception const &exception_) {
try { ::std::rethrow_if_nested(exception_); }
catch (...) { value_ = ::std::current_exception(); }
}
catch (...) {}
}
return *this;
}
inline auto operator++(int) noexcept(true) { auto const temporary_ = *this; ++(*this); return temporary_; }
inline auto operator==(Iterator const &other) const noexcept(true) { return value_ == other.value_; };
inline auto operator!=(Iterator const &other) const noexcept(true) { return not (*this == other); };
Iterator & operator=(Iterator &&) noexcept(true) = default;
Iterator & operator=(Iterator const &) noexcept(true) = default;
Iterator() noexcept(true) = default;
Iterator(Iterator &&) noexcept(true) = default;
Iterator(Iterator const &) noexcept(true) = default;
inline explicit Iterator(Value const &value) noexcept(true): value_{value} {}
private:
value_type value_;
};
inline auto Class::begin() const noexcept(true) { return Iterator{last_}; }
inline auto Class::end() const noexcept(true) { return Iterator{}; }
template <class T> inline decltype(auto) Class::make_value_(T &&value) noexcept(true) {
if constexpr (::std::is_same_v<::std::exception_ptr, ::std::decay_t<decltype(value)>>) return ::std::forward<decltype(value)>(value);
else return ::std::make_exception_ptr(::std::forward<decltype(value)>(value));
}
template <class headT, class ... tailT> requires((0 < sizeof ... (tailT)) or (not ::std::is_base_of_v<Class, ::std::decay_t<headT>>))
inline Class::Class(headT &&head, tailT && ... tail) noexcept(true): last_{::exception::walker::Class::make_value_(::std::forward<headT>(head), ::std::forward<tailT>(tail) ...)} {}
template <class ... T> inline auto make(T && ... payload) { return Class{::std::forward<T>(payload) ...}; }
} // namespace exception::walker
// from <.../exception/backtrace.hpp>
namespace exception::backtrace {
namespace private_ {
// in fact, it would be more correct to use const lvalue to T (T const &source) instead universal reference (T &&), but then
// ::other::namespace::make function may be used (::exception::walker::make for example if ::std::decay_t<sourceT> will be ::exception::walker::Class)
template <class sourceT> inline auto make(void const *, sourceT const &source) {
using CollectorItem = ::std::decay_t<decltype(*::std::begin(source))>;
auto collector_ = ::std::list<CollectorItem>{};
auto size_ = static_cast<::std::size_t>(0);
for (auto const &exception_ : source) { size_++; collector_.push_front(exception_); }
auto result_ = Class(size_);
::std::copy(collector_.begin(), collector_.end(), result_.begin());
return result_;
}
template <class T> inline auto make(::std::exception const *, T &&exception) {
// sad, but without knowledge about content of namespace in which the real type of T is located, I have to write here explicitly ::exception::backtrace::private_::make
return ::exception::backtrace::private_::make(static_cast<void const *>(nullptr), ::exception::walker::make(::std::forward<T>(exception)));
}
template <class T> inline auto make(::std::exception_ptr const *, T &&exception) {
// sad, without knowledge about content of namespace in which the real type of T is located, I have to write here explicitly ::exception::backtrace::private_::make
return ::exception::backtrace::private_::make(static_cast<void const *>(nullptr), ::exception::walker::make(::std::forward<T>(exception)));
}
} // namespace private_
template <class T> inline auto make(T &&source) {
// sad, but without knowledge about content of namespace in which the real type of T is located, I have to write here explicitly ::exception::backtrace::private_::make
return ::exception::backtrace::private_::make(static_cast<::std::decay_t<T> const *>(nullptr), ::std::forward<T>(source));
}
template <class beginT, class endT> inline auto make(beginT &&begin, endT &&end) {
// sad, but without knowledge about content of namespace in which the real type of ::std::ranges::subrange result is located, I have to write here explicitly ::exception::backtrace::make
return ::exception::backtrace::make(::std::ranges::subrange(::std::forward<beginT>(begin), ::std::forward<endT>(end)));
}
} // namespace exception::backtrace
namespace test_ {
inline static auto internal() { throw ::std::runtime_error{"internal function error"}; }
inline static auto external() {
try { internal(); }
catch(...) { ::std::throw_with_nested(::std::runtime_error{"external function error"}); }
}
}
int main(int, char **) {
try { test_::external(); }
catch(...) {
for (auto const &exception_: ::exception::backtrace::make(::std::current_exception())) {
try { ::std::rethrow_exception(exception_); }
catch(::std::exception const &exception_) { ::std::clog << exception_.what() << ::std::endl << ::std::flush; }
catch(...) { ::std::clog << "unknown exception" << ::std::endl << ::std::flush; }
}
}
return 0;
}
This is due to Argument dependent lookup.
If a function takes a parameter of a custom type, the namespace that the type is declared in is searched for viable overloads when calling a function.
f(a::f(value))
Is passing a C to f. This triggers ADL, thus additionally to doing name lookup in the surounding scopes, the compiler also searches in namespace a since C is declared there.
This is most commonly an important feature when defining free-function operator overloads. We can put them in the same namespace as the type in question, and ADL will make sure the overload is found at name-lookup without polluting the global/outer scopes.
Related
I'm just a simple coder who used Python a lot and got addicted to its generators. As far as I understand the current situation, they can be cleanly implemented in C++20 with coroutines, however, at least until C++23, it's not a trivial task since one needs to write a generator class (template). How do I get one that
Runs reasonably fast (at least not slower than the good old generators hack with macros)
I can use ranged-based for, the ranges library and some equivalent of Python's next on it. It would also be great if there was a method to test whether the generator is exhausted.
When (if?) an equivalent is added to the standard library, I (with high probability) won't need to change my code too much
Is this possible at all?
As mentioned in the comment, libcoro provides a higher level of abstraction and might solve some of your issues.
For point2, if you really need a public method that tells you the generator is exhausted, I guess enhancing libcoro generator would make it somehow easy. Here is a (not tested) possible sample. But is checking against generator.end() a problem for you?
namespace libcoro {
template<typename T>
class [[nodiscard]] generator
{
public:
using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
//.. libcoro stuff
// ADDED
bool done() const {
m_coroutine.done();
}
reference_type value() const {
return m_coroutine.promise().value();
}
void resume() {
m_coroutine.resume();
}
// ...
};
}
Then you can do:
while (true) {
gen.resume();
if(gen.done()) {
std::cout << "this is the end!" << std::endl;
break;
}
std::cout << "new value: " << gen.value() << std::endl;
}
Here you can find some reasonably fast generator implementation which prefers a std implementation if available (like in MSVC):
In my real code it is splitted in multiple files, but to show it here I merged it...
It works with all major comilers (gcc, clang, msvc).
//------------------
//general coroutine support
//------------------
#if defined(__clang__)
//see: https://developercommunity.visualstudio.com/content/problem/502513/unable-to-use-clang-cl-coroutines-due-to-unresolve.html
namespace std {
namespace experimental {
inline namespace coroutines_v1 {
template <typename R, typename...>
struct coroutine_traits {
using promise_type = typename R::promise_type;
};
template <typename Promise = void>
struct coroutine_handle;
template <> struct coroutine_handle<void> {
static coroutine_handle from_address(void* addr) noexcept {
coroutine_handle me;
me.ptr = addr;
return me;
}
void operator()() { resume(); }
void* address() const { return ptr; }
void resume() const { __builtin_coro_resume(ptr); }
void destroy() const { __builtin_coro_destroy(ptr); }
bool done() const { return __builtin_coro_done(ptr); }
coroutine_handle& operator=(decltype(nullptr)) {
ptr = nullptr;
return *this;
}
coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
coroutine_handle() : ptr(nullptr) {}
// void reset() { ptr = nullptr; } // add to P0057?
explicit operator bool() const { return ptr; }
protected:
void* ptr;
};
template <typename Promise>
struct coroutine_handle : coroutine_handle<> {
using coroutine_handle<>::operator=;
using coroutine_handle<>::coroutine_handle;
static coroutine_handle from_address(void* addr) noexcept {
coroutine_handle me;
me.ptr = addr;
return me;
}
Promise& promise() const {
return *reinterpret_cast<Promise*>(
__builtin_coro_promise(ptr, alignof(Promise), false));
}
static coroutine_handle from_promise(Promise& promise) {
coroutine_handle p;
p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
return p;
}
};
template <typename _PromiseT>
bool operator==(coroutine_handle<_PromiseT> const& _Left,
coroutine_handle<_PromiseT> const& _Right) noexcept
{
return _Left.address() == _Right.address();
}
template <typename _PromiseT>
bool operator!=(coroutine_handle<_PromiseT> const& _Left,
coroutine_handle<_PromiseT> const& _Right) noexcept
{
return !(_Left == _Right);
}
struct suspend_always {
bool await_ready() noexcept { return false; }
void await_suspend(coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
struct suspend_never {
bool await_ready() noexcept { return true; }
void await_suspend(coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
}
}
}
#define NATIVE_COROUTINE_IMPL_NS std::experimental
#elif __has_include(<coroutine>)
#include <coroutine>
#define NATIVE_COROUTINE_IMPL_NS std
#elif __has_include(<experimental/coroutine>)
#include <experimental/coroutine>
#define NATIVE_COROUTINE_IMPL_NS std::experimental
#endif
//------------------
//Generator defintion
//-----------------
#if __has_include(<generator>)
#include <generator>
namespace CORE_NATIVE_NS
{
template<typename T>
using YieldEnum = std::generator<T>;
}
#elif __has_include(<experimental/generator>) && !defined(__clang__)
#include <experimental/generator>
namespace CORE_NATIVE_NS
{
template<typename T>
using YieldEnum = std::experimental::generator<T>;
}
#else
#include <memory>
#include <cstddef>
#include <type_traits>
#include <utility>
#include <exception>
#include <iterator>
#include <functional>
namespace CORE_NATIVE_NS
{
template<typename T>
class generator;
namespace detail
{
template<typename T>
class generator_promise
{
public:
using value_type = std::remove_reference_t<T>;
using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
using pointer_type = value_type*;
generator_promise() = default;
generator<T> get_return_object() noexcept;
constexpr NATIVE_COROUTINE_IMPL_NS::suspend_always initial_suspend() const noexcept
{ return {}; }
constexpr NATIVE_COROUTINE_IMPL_NS::suspend_always final_suspend() const noexcept
{ return {}; }
template<
typename U = T,
std::enable_if_t<!std::is_rvalue_reference<U>::value, int> = 0
>
NATIVE_COROUTINE_IMPL_NS::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
{
m_value = std::addressof(value);
return {};
}
NATIVE_COROUTINE_IMPL_NS::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept
{
m_value = std::addressof(value);
return {};
}
void unhandled_exception() noexcept
{
m_exception = std::current_exception();
}
void return_void() const noexcept
{
}
reference_type value() const noexcept
{
return static_cast<reference_type>(*m_value);
}
// Don't allow any use of 'co_await' inside the generator coroutine.
template<typename U>
NATIVE_COROUTINE_IMPL_NS::suspend_never await_transform(U&& value) = delete;
void rethrow_if_exception()
{
if (m_exception)
{
std::rethrow_exception(m_exception);
}
}
private:
pointer_type m_value;
std::exception_ptr m_exception;
};
struct generator_sentinel {};
template<typename T>
class generator_iterator
{
using coroutine_handle = NATIVE_COROUTINE_IMPL_NS::coroutine_handle<generator_promise<T>>;
public:
using iterator_category = std::input_iterator_tag;
// What type should we use for counting elements of a potentially infinite sequence?
using difference_type = std::ptrdiff_t;
using value_type = typename generator_promise<T>::value_type;
using reference = typename generator_promise<T>::reference_type;
using pointer = typename generator_promise<T>::pointer_type;
// Iterator needs to be default-constructible to satisfy the Range concept.
generator_iterator() noexcept
: m_coroutine(nullptr)
{}
explicit generator_iterator(coroutine_handle coroutine) noexcept
: m_coroutine(coroutine)
{}
friend bool operator==(const generator_iterator& it, generator_sentinel) noexcept
{
return !it.m_coroutine || it.m_coroutine.done();
}
friend bool operator!=(const generator_iterator& it, generator_sentinel s) noexcept
{
return !(it == s);
}
friend bool operator==(generator_sentinel s, const generator_iterator& it) noexcept
{
return (it == s);
}
friend bool operator!=(generator_sentinel s, const generator_iterator& it) noexcept
{
return it != s;
}
generator_iterator& operator++()
{
m_coroutine.resume();
if (m_coroutine.done())
{
m_coroutine.promise().rethrow_if_exception();
}
return *this;
}
// Need to provide post-increment operator to implement the 'Range' concept.
void operator++(int)
{
(void)operator++();
}
void next(){
(void)operator++();
}
reference operator*() const noexcept
{
return m_coroutine.promise().value();
}
pointer operator->() const noexcept
{
return std::addressof(operator*());
}
private:
coroutine_handle m_coroutine;
};
}
template<typename T>
class [[nodiscard]] generator
{
public:
using promise_type = detail::generator_promise<T>;
using iterator = detail::generator_iterator<T>;
generator() noexcept
: m_coroutine(nullptr)
{}
generator(generator&& other) noexcept
: m_coroutine(other.m_coroutine)
{
other.m_coroutine = nullptr;
}
generator(const generator& other) = delete;
~generator()
{
if (m_coroutine)
{
m_coroutine.destroy();
}
}
generator& operator=(generator other) noexcept
{
swap(other);
return *this;
}
iterator begin()
{
if (m_coroutine)
{
m_coroutine.resume();
if (m_coroutine.done())
{
m_coroutine.promise().rethrow_if_exception();
}
}
return iterator{ m_coroutine };
}
detail::generator_sentinel end() noexcept
{
return detail::generator_sentinel{};
}
void swap(generator& other) noexcept
{
std::swap(m_coroutine, other.m_coroutine);
}
private:
friend class detail::generator_promise<T>;
explicit generator(NATIVE_COROUTINE_IMPL_NS::coroutine_handle<promise_type> coroutine) noexcept
: m_coroutine(coroutine)
{}
NATIVE_COROUTINE_IMPL_NS::coroutine_handle<promise_type> m_coroutine;
};
template<typename T>
void swap(generator<T>& a, generator<T>& b)
{
a.swap(b);
}
namespace detail
{
template<typename T>
generator<T> generator_promise<T>::get_return_object() noexcept
{
using coroutine_handle = NATIVE_COROUTINE_IMPL_NS::coroutine_handle<generator_promise<T>>;
return generator<T>{ coroutine_handle::from_promise(*this) };
}
}
template<typename FUNC, typename T>
generator<std::invoke_result_t<FUNC&, typename generator<T>::iterator::reference>> fmap(FUNC func, generator<T> source)
{
for (auto&& value : source)
{
co_yield std::invoke(func, static_cast<decltype(value)>(value));
}
}
template<typename T>
using YieldEnum = CORE_NATIVE_NS::generator<T>;
}
#endif
inline CORE_NATIVE_NS::YieldEnum<int> GetNumbers() noexcept
{
for(int i=0; i<= 10; ++i){
co_yield 10;
}
}
#include <iostream>
int main()
{
int sum = 0;
for(auto x : GetNumbers()){
sum += x;
}
std::cout << sum;
return 0;
}
I'm trying to help with this question, and I think I found a nice solution. But it's kind of complex due to all the wrapping I need.
I would want to overload operator>> to allow for easy chaining notation. But it's not working as I expected.
I can do:
#include <functional>
#include <memory>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T, typename MemFn>
auto fnc (T* ptr, MemFn memFn)
-> decltype(std::invoke(memFn, std::declval<T>())) {
if (ptr) return std::invoke(memFn, *ptr);
return nullptr;
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
[[maybe_unused]] int* optionalTarget = fnc(myObjectPtr.get(), &MyObject::Get);
}
However, I want to do
#include <functional>
#include <memory>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T, typename MemFn>
auto operator>> (T* ptr, MemFn memFn)
-> decltype(std::invoke(memFn, std::declval<T>())) {
if (ptr) return std::invoke(memFn, *ptr);
return nullptr;
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
[[maybe_unused]] int* optionalTarget = myObjectPtr.get() >> &MyObject::Get;
}
What does work, but what I consider ugly is
#include <functional>
#include <memory>
#include <optional>
template<typename T>
class Object {
private:
T* const ptr{ nullptr };
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using MyObject = Object<int>;
template <typename T>
auto makeOptRef(T* ptr) -> std::optional< std::reference_wrapper<T>> {
if (ptr) return std::ref(*ptr);
return {};
}
template <typename T, typename MemFn>
auto operator>> (std::optional<std::reference_wrapper <T>> ptr, MemFn memFn)
-> std::optional<std::reference_wrapper<std::remove_pointer_t<decltype(std::invoke(memFn, std::declval<T>()))>>> {
if (ptr) return makeOptRef(std::invoke(memFn, *ptr));
return {};
}
int main() {
std::unique_ptr<MyObject> myObjectPtr = std::make_unique<MyObject>(nullptr);
std::optional<std::reference_wrapper<MyObject>> myObjOptRef = makeOptRef(myObjectPtr.get());
std::optional<std::reference_wrapper<int>> optionalTarget = myObjOptRef >> &MyObject::Get;
[[maybe_unused]] int output = (optionalTarget) ? optionalTarget->get() : -1;
}
Any pointer type is a built-in type, as are pointers to members. You are trying to overload an operator for two built-in types, that's simply a non-starter. At least one argument must be of a user-defined type, or a reference to it.
You can get it to work by passing a T& instead of a T*.
Say I have a virtual base class Base, which will in part behave like a container, with two derived classes VectorLike and RangeLike.
I want to achieve something like the following:
class VectorLike : public Base {
std::vector<int> data;
public:
virtual std::vector<int>::const_iterator cbegin() { return data.cbegin() }
virtual std::vector<int>::const_iterator cend() { return data.cend() }
}
class RangeLike : public Base {
int min, max;
class const_iterator {
int x;
public:
int operator++() { return ++x }
bool operator==( const_iterator rhs ) { return x == rhs.x }
const_iterator( int y ) { x = y }
}
public:
virtual const_iterator cbegin() { return const_iterator( min ); }
virtual const_iterator cend() { return const_iterator( max ); }
}
This code will not compile, since std::vector<int>::const_iterator and RangeLike::const_iterator aren't identical or covariant.
To achieve the second, I would need an iterator base class from which both std::vector<int>::const_iterator and RangeLike::const_iterator will derive. But then still cbegin() and cend() will have to return pointers to iterators, which will make an even bigger mess.
My question is, is it possible to achieve something like the above code and if so how?
Here is an implementation of a polymorphic const int iterator. You can construct it with any iterator type (including pointers) where std::iterator_traits<Iter>::value_type resolves to int.
This should be the case for both std::vector<int> and your_range_type<int>.
This should get you started.
#include <iostream>
#include <vector>
#include <array>
#include <memory>
#include <algorithm>
struct poly_const_iterator
{
using value_type = int;
struct concept {
virtual void next(int n) = 0;
virtual const value_type& deref() const = 0;
virtual bool equal(const void* other) const = 0;
virtual std::unique_ptr<concept> clone() const = 0;
virtual const std::type_info& type() const = 0;
virtual const void* address() const = 0;
virtual ~concept() = default;
};
template<class Iter>
struct model : concept
{
model(Iter iter) : _iter(iter) {}
void next(int n) override { _iter = std::next(_iter, n); }
const value_type& deref() const override { return *_iter; }
bool equal(const void* rp) const override { return _iter == static_cast<const model*>(rp)->_iter; }
std::unique_ptr<concept> clone() const override { return std::make_unique<model>(*this); }
const std::type_info& type() const override { return typeid(_iter); }
const void* address() const override { return this; }
Iter _iter;
};
std::unique_ptr<concept> _impl;
public:
// interface
// todo: constrain Iter to be something that iterates value_type
template<class Iter>
poly_const_iterator(Iter iter) : _impl(std::make_unique<model<Iter>>(iter)) {};
poly_const_iterator(const poly_const_iterator& r) : _impl(r._impl->clone()) {};
const value_type& operator*() const {
return _impl->deref();
}
poly_const_iterator& operator++() {
_impl->next(1);
return *this;
}
bool operator==(const poly_const_iterator& r) const {
return _impl->type() == r._impl->type()
and _impl->equal(r._impl->address());
}
bool operator != (const poly_const_iterator& r) const {
return not(*this == r);
}
};
void emit(poly_const_iterator from, poly_const_iterator to)
{
std::copy(from, to, std::ostream_iterator<int>(std::cout, ", "));
std::cout << std::endl;
}
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::array<int, 5> a = { 6, 7,8, 9, 0 };
emit(std::begin(v), std::end(v));
emit(std::begin(a), std::end(a));
return 0;
}
expected results:
1, 2, 3, 4, 5,
6, 7, 8, 9, 0,
Here's a C++20 based solution (partial implementation)... just thought I'd put it out there.
It uses a variant with stack storage rather than polymorphism and dynamic allocation, so it might have better performance.
It should work for any given set of iterator types that have a common value_type and whose reference type is value_type&. It should also be fairly easy to adapt for other types of iterator and to include operator->.
Not claiming this is better than the implementation in the original answer. Merely an interesting alternative...
PS. normal in the names below simply indicates a basic pointer based iterator.
template <typename _Iterator, typename _Value, typename _Reference, typename _Difference>
concept forward_iterator_for
= std::forward_iterator<_Iterator>
&& std::same_as<std::iter_value_t<_Iterator>, _Value>
&& std::same_as<std::iter_reference_t<_Iterator>, _Reference>
&& std::same_as<std::iter_difference_t<_Iterator>, _Difference>;
template <typename _Iterator, typename _Value>
concept normal_forward_iterator_for //
= std::same_as<std::remove_cvref_t<_Value>, std::remove_const_t<_Value>>
&& forward_iterator_for<_Iterator, std::remove_const_t<_Value>, _Value&, std::ptrdiff_t>;
template <typename _Value, normal_forward_iterator_for<_Value> ... _Iterator>
class normal_forward_iterator_variant {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::remove_cv_t<_Value>;
using reference = _Value&;
using iterator_type = std::variant<_Iterator...>;
private:
iterator_type _m_iter;
public:
normal_forward_iterator_variant() = default;
template <normal_forward_iterator_for<_Value> _Iter, typename... _Args>
normal_forward_iterator_variant(std::in_place_type_t<_Iter>, _Args&&... args)
noexcept (std::is_nothrow_constructible_v<_Iter, _Args&&...>)
: _m_iter(std::in_place_type<_Iter>, std::forward<_Args>(args)...) {}
normal_forward_iterator_variant(iterator_type const& iter)
noexcept (std::is_nothrow_copy_constructible_v<iterator_type>)
: _m_iter(iter) {}
normal_forward_iterator_variant(normal_forward_iterator_variant const&) = default;
normal_forward_iterator_variant(normal_forward_iterator_variant&&) = default;
constexpr normal_forward_iterator_variant&
operator=(normal_forward_iterator_variant const& iter)
noexcept (std::is_nothrow_copy_assignable_v<iterator_type>) //
requires std::is_copy_assignable_v<iterator_type> {
_m_iter = iter._m_iter;
return *this;
}
constexpr normal_forward_iterator_variant&
operator=(normal_forward_iterator_variant&& iter)
noexcept (std::is_nothrow_move_assignable_v<iterator_type>) //
requires std::is_move_assignable_v<iterator_type> {
_m_iter = std::move(iter._m_iter);
return *this;
}
template <typename _Tp>
constexpr normal_forward_iterator_variant&
operator=(_Tp const& x)
noexcept (std::is_nothrow_assignable_v<iterator_type, _Tp&>) //
requires std::is_assignable_v<iterator_type, _Tp&> {
_m_iter = x;
return *this;
}
template <typename _Tp>
constexpr normal_forward_iterator_variant&
operator=(_Tp&& x)
noexcept (std::is_nothrow_assignable_v<iterator_type, _Tp&&>) //
requires std::is_assignable_v<iterator_type, _Tp&&> {
_m_iter = std::move(x);
return *this;
}
[[nodiscard]] constexpr reference
operator*() const noexcept {
return std::visit([](auto&& iter) -> reference {
return *iter;
}, _m_iter);
}
constexpr normal_forward_iterator_variant&
operator++() noexcept {
std::visit([](auto&& iter) {
++iter;
}, _m_iter);
return *this;
}
constexpr normal_forward_iterator_variant
operator++(int) noexcept {
normal_forward_iterator_variant rv(*this);
++*this;
return rv;
}
[[nodiscard]] friend constexpr bool
operator==(normal_forward_iterator_variant const& a, normal_forward_iterator_variant const& b) noexcept {
return (a._m_iter == b._m_iter);
}
};
Example use:
#include <iostream>
#include <iomanip>
#include <vector>
#include <list>
#include <iterator.h>
int
main() {
using vector = std::vector<int>;
using list = std::list<int>;
using iterator = normal_forward_iterator_variant<const int, vector::const_iterator, list::const_iterator>;
iterator iter;
vector v{ 0, 1, 2 };
iter = v.cbegin();
std::cout << *iter++ << std::endl;
std::cout << *iter << std::endl;
std::cout << *++iter << std::endl;
list l{ 3, 4, 5 };
iter = l.cbegin();
std::cout << *iter++ << std::endl;
std::cout << *iter << std::endl;
std::cout << *++iter << std::endl;
}
Output is 1, 2, ..., 6 as expected.
How would I go about making a function that uses braces like if/for/while statements? I'm referring to this as a 'keyword statement' because I don't know what else to call it.
Meaning, for example, if I wanted to make a 'repeat' function:
repeat(3)
{
//do something
}
I guess a better question is, is this possible? If so, how would one go about doing this?
Don't do that [#define repeat] - don't try to change the syntax of the programming language you're using. That will make your code far less readable for anyone else.
You might define a range similar to a python range:
// Range
// =====
#include <iterator>
#include <utility>
template<typename T>
class Range
{
public:
typedef T value_type;
public:
class iterator
{
public:
typedef typename std::forward_iterator_tag iterator_category;
typedef typename std::size_t size_type;
typedef typename std::ptrdiff_t difference_type;
typedef T value_type;
typedef const T& reference;
typedef const T* pointer;
public:
iterator(const T& value) noexcept
: m_value(value)
{}
reference operator * () const noexcept { return m_value; }
pointer operator -> () const noexcept { return &m_value; }
iterator& operator ++ () noexcept { ++m_value; return *this; }
friend bool operator == (const iterator & a, const iterator b) noexcept {
return a.m_value == b.m_value;
}
friend bool operator != (const iterator & a, const iterator b) noexcept {
return a.m_value != b.m_value;
}
private:
T m_value;
};
public:
Range(const T& first, const T& last) noexcept
: m_first(first), m_last(last)
{}
Range(T&& first, T&& last) noexcept
: m_first(std::move(first)), m_last(std::move(last))
{}
Range(Range&& other) noexcept
: m_first(std::move(other.m_first)),
m_last(std::move(other.m_last))
{}
Range& operator = (Range&& other) noexcept {
m_first = std::move(other.m_first);
m_last = std::move(other.m_last);
return *this;
}
iterator begin() const noexcept { return m_first; }
iterator end() const noexcept { return m_last; }
private:
T m_first;
T m_last;
};
template<typename T>
inline Range<T> range(T&& first, T&& last) noexcept {
return Range<T>(std::move(first), std::move(last));
}
// Test
// ====
#include <iostream>
int main() {
for(auto i : range(0, 3))
std::cout << i << '\n';
}
A more sophisticated implementation would consider containers and iterators, too.
You could define a macro taking 1 argument:
#define repeat(COUNT) \
for (unsigned int i = 0; i < (COUNT); ++i)
and leave the brakets empty after it, the preprocessor will expand the following example:
repeat(3)
{
//do something
}
into:
for (unsigned int i = 0; i < (3); ++i)
{
//do something
}
Demo
Due to some legacy reasons I'm stuck with MIPS-GCC 4.5.3. But the code which I'm trying to compile uses C++11 nullptr & nullptr_t heavily which is a missing feature in GCC 4.5.3.
After some googling & getting into the usage I ended up creating a nullptr wrapper like below, but unfortunately it doesn't satisfy some of the use case,
namespace std {
class nullptr_t {
public:
nullptr_t() { }
template <typename T> nullptr_t(const T&) { }
template <class T> nullptr_t(const T*) { }
template <class T> nullptr_t(T*) { }
template <typename T, typename U> nullptr_t(const typename T::U*) { }
template<typename T> operator T*() { return 0;}
template<typename T1, typename T2> operator T1 T2::*() { return 0; }
operator int() const { return 0; }
operator unsigned() const { return 0; }
operator bool() const { return false; }
bool operator == (unsigned i) const { return i == 0; }
bool operator != (unsigned i) const { return i != 0; }
bool operator !() const { return true; }
} nullptr = {};
}
using std::nullptr;
template<typename T> struct DummyContainer {
DummyContainer(T* ptr)
: m_ptr(ptr) { }
DummyContainer(std::nullptr_t)
: m_ptr(0) { }
T& operator = (std::nullptr_t) { return *m_ptr; }
private: T* m_ptr;
};
int main(int argc, char** argv)
{
const char* case1 = nullptr; // working
// I think for below case std::unique_ptr has to be modified to take std::nullptr_t during construction & operator =
std::unique_ptr<char> case2 = nullptr; // not working.
DummyContainer<char> case3 = nullptr; // working
case3 = nullptr; //working
unsigned* case4 = argc > 1 ? nullptr : nullptr; //works
unsigned* case5 = argc > 2 ? (unsigned*)0 : nullptr; //not working. (It is the major issue as of now)
return 0;
}
Here the major case is unsigned* case5 = argc > 2 ? (unsigned*)0 : nullptr;
IDEONE snapshot : http://ideone.com/m1mhtB
(Thanks to http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf)
Any tips/suggestions will be appreciated :)
(Note: please avoid answers like upgrade your gcc)
Below solution seems to be working,
Original source: https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/wtf/NullPtr.h
namespace std {
class nullptr_t {
public:
// Required in order to create const nullptr_t objects without an
// explicit initializer in GCC 4.5, a la:
//
// const std::nullptr_t nullptr;
nullptr_t() { }
// Make nullptr convertible to any pointer type.
template<typename T> operator T*() const { return 0; }
// Make nullptr convertible to any member pointer type.
template<typename C, typename T> operator T C::*() { return 0; }
private:
// Do not allow taking the address of nullptr.
void operator&();
};
}
const std::nullptr_t nullptr;
IDEOne: http://ideone.com/Bnp6th