GithubHelp home page GithubHelp logo

cpp_proposals's People

Contributors

andralex avatar atomgalaxy avatar brevzin avatar codereport avatar daveedvdv avatar jwakely avatar katzdm avatar mpark avatar pdimov avatar tartanllama avatar tomaszkam avatar ukilele avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

cpp_proposals's Issues

Feedback on 'P2387R3: Pipe support for user-defined range adaptors'

Hello Barry,
I've recently implemented in C++20 a custom range adaptor cxx::chunk_evenly():

While doing so, I've realized that, in order to have the ability to use the pipe operator,
I need to implement the std::ranges::range_adaptor_closure class template from P2387R3:

After getting myself familiar with P2387R3, I want to say that,
I am strongly in favor of it and glad that it was accepted into the C++23 draft.
Finally all C++ developers will have the ability to implement custom range adaptors,
which will be composable with standard range adaptors!

However, even though the cxx::ranges::range_adaptor_closure_interface class template
does not support composing, via the pipe operator, custom range adaptor closures
with standard range adaptor closures, and therefore, when creating a pipeline,
the cxx::chunk_evenly() range adaptor is not composable with standard range adaptors:

it still has a couple of advantages, compared to the std::ranges::range_adaptor_closure,
which I think are worth adopting:

  • more suitable name
    The std::ranges::range_adaptor_closure is not a range adaptor closure itself.
    It is a class template, which range adaptor closures (or rather types,
    which aim to model the range_adaptor_closure concept) should inherit from CRTP-style.

    Since that class template provides essential part of the interface,
    that all range adaptor closures are characterized by,
    that is invocability and composability via the pipe operator,
    it should be renamed to std::ranges::range_adaptor_closure_interface.

    Please notice that, the proposed new name will be consistent with
    the std::ranges::view_interface class template,
    which has a similar role in the context of the std::ranges::view concept.

    Moreover the name std::ranges::range_adaptor_closure should be
    reserved for the range_adaptor_closure concept,
    which represents the set of types of all range adaptor closure objects.

  • constrained pipe operators
    The definition of the std::ranges::range_adaptor_closure concept
    is actually already specified in 26.7.2 [range.adaptor.object].

    Unfortunately defining the std::ranges::range_adaptor_closure concept in practice
    is incredibly difficult, since it requires defining an archetype for
    the std::ranges::viewable_range concept, which I just was not able to do.

    Side Note:

    Since, a range adaptor object is a customization point object
    that accepts a viewable_range as its first argument and returns a view,
    a range adaptor closure object should be a unary function object
    that accepts a viewable_range as its only argument.

    Fortunately I was able to define two (exposition-only) concepts:

    1. cxx::ranges::maybe_range_adaptor_closure
    2. cxx::ranges::range_adaptor_for<viewable_range>

    and at least partially constrain the pipe operators using them.

    Ideally the std::ranges::range_adaptor_closure concept would be added to
    the standard library, since it would be useful for defining customizable pipelines:

    auto create_pipeline (std::ranges::range_adaptor_closure auto&& op)
    {
        return std::views::take(2) | op | std::views::reverse;
    }
    

    However, this can be done in a future C++ standard and
    requiring the std::ranges::range_adaptor_closure_interface class template
    to provide at least partially constrained pipe operators is better than nothing.

I hope this feedback is valuable and
it is not tool late to at least rename the std::ranges::range_adaptor_closure
to std::ranges::range_adaptor_closure_interface before C++23 will approved and released.

Thank you, Mateusz Zych

The constexpr array size problem

I’m getting around this problem so far.

    template <typename T, size_t N>
    struct array_size {
        constexpr array_size(T (&)[N]) { }
        constexpr array_size(T(&&)[N]) { }
        constexpr operator size_t() { return N; }
    };
    template <typename T, size_t N>
    array_size(T (&)[N]) -> array_size<T, N>;
    template <typename T, size_t N>
    array_size(T(&&)[N]) -> array_size<T, N>;

    void check(int const (&param)[3])
    {
        int local[] { 1, 2, 3 };
        [[maybe_unused]] constexpr size_t s0 = array_size(local); // ok
        [[maybe_unused]] constexpr size_t s1 = array_size(decltype(param) {}); // ok
    }

Working code from my project.

    template <class T>
    bool writeMultipleCoils(uint16_t regAddress, T& coils) requires std::is_array_v<T>
    {
...
        constexpr size_t coilsCount = array_size(T {});
        constexpr uint8_t dataSize = coilsCount / 8 + (coilsCount % 8 ? 1 : 0);
        uint8_t data[dataSize] {};
...
    }

Add selead classes

I'm very impressed with the huge contribution you make to C++.
I want to offer you an idea for improving C++.

Often during developing, it is necessary to create a hierarchy with one base class and a finite number of descendants.

class figure {
public:
    virtual ~figure() = default;

    virtual int square() = 0;
};

struct rectangle final : public figure {
    int square() override {
        return 2 * width + 2 * height;
    }


    int width, height;
};

struct circle final : public figure {
    int square() override {
        return 2 * M_PI * radius;
    }

    int radius;
};

The function to get the area of any of the figures can be described as follows

int square(const figure& f) {
    if (typeid(f) == typeid(rectangle)) {
        return static_cast<rectangle&>(f).square();
    } else if (typeid(f) == typeid(circle)) {
        return static_cast<rectangle&>(f).square();
    } else {
        assert(false);
    }
}

Since virtual functions, RTTI are used, this leads to code performance degradation.
As an output, it could use std::variant and std::visit.
The disadvantage of std::variant is that polymorphism must be abandoned.
https://en.cppreference.com/w/cpp/utility/variant

If the class hierarchy cannot be abandoned, CRTP can be used.
But this approach also has disadvantages:
- no redefinition of virtual functions;
- it is not easy to read the code compared to conventional virtual inheritance;
- possible binary bloat due to templates.
https://en.cppreference.com/w/cpp/language/crtp

Propose to add the sealed keyword, which can be used to mark the base (abstract) class.
It indicates that the given base (abstract) class has a finite number of descendants.
And instead of a virtual table under the hood, compiler could use a static array of descendants.

class figure sealed {   // similar - final
public:
    virtual ~figure() = default;

    virtual int square() = 0;
};

Since the compiler knows a finite number of descendants, it can optimize the code more strongly.
For example, do not use RTTI at all. Or make typeid work at compile time.

int square(const figure& f) {
    if (typeid(f) == typeid(rectangle)) {
        return static_cast<rectangle&>(f).square();
    } else if (typeid(f) == typeid(bad_figure)) {       // compile time error: no branch with `circle`
        return static_cast<rectangle&>(f).square();
    }
}

Also it don't need to write an else branch. If you skip at least one of the descendants in the if-else-if chain, then the compiler at compile time could tell, which successor was skipped.

Similar classes have been added to Java, Kotlin, Scala.
https://docs.oracle.com/en/java/javase/15/language/sealed-classes-and-interfaces.html
https://kotlinlang.org/docs/sealed-classes.html

sealed classes are similar to enum types in Rust, Swift.
https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html

This is similar to std::variant, but only at the language level. I very want to see such a feature in C++.

Integrate Bronek's suggestions into P0847

4.2.1 should also address what happens if class contains both styles of a single function with the same "this" type. e.g.

struct X {
void bar() &&;
void bar(this X&&);
};


I guess this should be ill-formed, unable to resolve overloading? What about mixing forms:

struct X {
void bar() &;
void bar() const &;
void bar(this X&&);
};

If I read your proposal right, this should be fine. Would be nice to acknowledge it.


Finally what about

struct X {
void bar(this X&);
void bar(this const X&);
void bar(this X&&);
template void bar(this S&&);
};


4.2.4 Example with the body of function f5 returning field "self.B::i" is obviously the weak point of this proposal, due to lack of succinct syntax to do an obvious thing. It is also missing examples of bugs that the user might encounter when they forget the rules - it appears that the worst that can happen is compilation error, but would be good to expand on this.


4.2.5 bad formatting in the list "Types are as follows" :^) Also, this might be a good place to refer to unified call syntax, perhaps we can reconsider making a pointer to member function callable? If so that would be different paper, obviously.


5.3 lambda example, I think the following would work as well:

// this proposal
auto fib = [](this auto self, int n) {
if (n < 2) return n;
return self(n-1) + self(n-2);
};

Since there is no capture in this particular lambda, there should be no need for "const&" (or "&&") in deduced this parameter. Perhaps this could be extended upon in section 5.4 as well (add 5.4.3 for capture-less lambdas?)


The other lambda example (tree traversal), I suggest adding some definition of Leaf at the top because we are accustomed to self-contained examples, e.g.:
using Leaf = int;


The part "self isn't the lambda, self is the overload wrapper" is IMO rather novel and a great point to score, so rather than fancy graph-like comment I'd prefer if that interaction between lambda with explicit this parameter and generic overload was explored in more depth.

Links in README should be to rendered versions

The links from README.md give us a page with the proposal source. Which is basically a lot of boilerplate CSS before we get anywhere.

Instead, the links should lead to the HTML of the proposal, with a text/HTML MIME type, so that the browser would actually display it.

p1065r1 issues

From Tim:

  • There's a hanging {.pnum}
  • The static_cast in func.bind.bind is missing a td_i (the thing we're casting)
  • "FD and each TDi meets the of Cpp17MoveConstructible and Cpp17Destructible requirements" has an extra "of"

Put Peter's ref-qualification suggestsion into p0847

Just pasting my reply here so we don't forget

Hi Peter,

Thanks for reviewing our examples!

Responses inline.

On Wed, Jul 22, 2020 at 8:28 AM Peter Sommerlad [email protected] wrote:

Hi all,

Sorry for jumping very late on the bus, but as always, there is too much
happening in WG21 to be able to pay attention everywhere

I know how you feel :).

I would like to talk you into providing consistent ref-qualification in
all your code examples returning references/pointers/iterators to the
guts of an object, so that the chance for returning a referring thing to
a temporary, that will immediately dangle, is reduced.(*)

We'll do that in all examples where it makes sense - in some, we try to critique the current status quo, which is wrong, as you rightly point out.

You do that in cases of std::optional, but not in the other examples

i.e.

class TextBlock {
public:
   char const& operator[](size_t position) const {
     // ...
     return text[position];
   }

   char& operator[](size_t position) {
     return const_cast<char&>(
       static_cast<TextBlock const&>
         (this)[position]
     );
   }
   // ...
};

should read imho:

class TextBlock {
public:
   char const& operator[](size_t position) const & {
     // ...
     return text[position];
   }

   char& operator[](size_t position) & {
     return const_cast<char&>(
       static_cast<TextBlock const&>
         (this)[position]
     );
   }
   // ...
};

Yes, that should be fixed, IMO. However, you will note that with deducing this, it's impossible to write the un-ref qualified pathological cases. I believe this improves safety.

That leads to the question, how to specify the 'this Self&& self'
parameter in your replacement, because calling it on a temporary would
lead to deducing an rvalue reference, this returning a reference to a
temporary.

Yes, the deduction to rvalue reference is a feature, not a bug. That said, the guidance for what to return from functions called on temporaries would do well to take your point under advisement.

So should this read then instead like the following?

class TextBlock {
public:
   template <typename Self>
   auto& operator[](this Self& self, size_t position) {
     // ...
     return self.text[position];
   }
   // ...
};

No, it should not read as Self& - you can't deduce const& from Self&. I think a more plausible alternative is something like this, if you want to return by value for temporarires:

template <typename Self, typename Return>
using nodangle_forward_ret_t = std::conditional_t<std::is_lvalue_reference_v<Self&&>, std::copy_cvref_t<Self&&, Return>, Return>;

class TextBlock {
public:
template
auto operator[](this Self&& self, size_t position)
-> nodangle_forward_ret_t<decltype(self), char> {
// ...
return std::forward(self).text[position];
}
// ...
};
copy_cvref_t is proposed in this mailing: http://wg21.link/p1450
I believe this highlights a point - for code that wants to return by value for temporaries instead of returning a rvalue-reference for such accessors (which IMO is legitimate, but a teensy bit more likely to dangle), we should probably standardize a convenient type-trait and forwarder so we can have the idiomatic usage be easy.

Add std::make_unique_nothrow and std::make_shared_nothrow functions

Looking at cppreference, the implementations of the std::make_unique() and std::make_shared() functions use a simple call of the new operator.
https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared

And the implementations of the three main compilers confirm this.
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/unique_ptr.h
https://github.com/llvm/llvm-project/blob/main/libcxx/include/__memory/unique_ptr.h
https://github.com/microsoft/STL/blob/main/stl/inc/memorу

Using std::make_unique() and std::make_shared() can result to a std::bad_alloc exception, being thrown if there is not enough memory in system.
In an environment where exceptions are disabled, it must use the new operator with std::nothrow.

std::unique_ptr<T> p = new(std::nothrow) T();

The std::make_shared() and std::make_unique() functions are more efficient and could prevent double allocations.
It is proposed to add std::make_unique_nothrow() and std::make_shared_nothrow() functions, which could be implemented as follow.

template <class T, class... Args>
std::unique_ptr<T> make_unique_nothrow(Args&&... args)
    noexcept(noexcept(T(std::forward<Args>(args)...)))
{
    return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
}

template <class T, class... Args>
std::shared_ptr<T> make_shared_nothrow(Args&&... args)
    noexcept(noexcept(T(std::forward<Args>(args)...)))
{
    return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
}

The boost library already implements similar functions.
https://www.boost.org/doc/libs/1_63_0/boost/move/make_unique.hpp

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.