GithubHelp home page GithubHelp logo

kdalgorithms's Issues

Range-ify the code

We cannot pretend that ranges don't exist. While rightfully one does NOT want to reimplement the algorithms themselves¹, one should still

  • reason in terms of iterator/sentinel pairs, instead of iterator/iterator
  • detect if <ranges> is available
  • if so, use the algorithm overloads in std::ranges (as they're the only ones that support iterator/sentinel)

Example:

QRegularExpression re = "\\d+";
auto matches = re.globalMatch(string);
auto matchesLengths = KDAlgorithms::transform(matches, [](const auto &m) { return m.capturedLength(); });

¹ even if simple (e.g. find_if). In other words, ranges should be supported up to the algorithmic call, after that one can require the user to be using a C++20 standard library.

work with std::range::subrange

Possibly related to #4 is it possible to make the algorithms work with subranges?

Occasionally I need to use an algorithm on a subset of a container. I like using kdalgorithms rather than STL because kdalgorithms::transformed for example returns the new container rather than requiring it to be declared in advance.

I tried this; all_of compiles, but transformed does not, complaining about missing value_type in the subrange (in MSVC 2022):

	std::vector<int> test;
	auto x = kdalgorithms::all_of(std::ranges::subrange(test.cbegin(), test.cend()),
		[](const int i) -> bool { return i == 0; });

	auto y = kdalgorithms::transformed<std::vector<int>>(std::ranges::subrange(test.cbegin(), test.cend()),
		[](const int i) -> int { return i + 1; });

Constrain the boolean operators

The convenience operators should be constrained to only take predicates or similar. Otherwise, if they're in scope, then all sorts of nonsense starts compiling:

using namespace KDAlgorithms::Operators;
std::string to_be;

to_be || !to_be; // that is the question

(Granted, one can't really use the return value of these operators, as it would immediately then trigger compilation errors)

find_if creates dangling references for r-value references.

kdalgorithms::find_if if called with a temporary r-value reference, results in name returning a reference to the collection, which has already been deleted.

find_if only has an overload for l-value references, which returns an iterator result.
With l-values, this works fine, but if used with an r-value reference, as in the example below, the collection will be deleted as soon as the find_if has finished executing.
This means any dereference of the returned name may segfault (in my case it didn't until a few days after implementing this 🙈 ).

Ideally find_if should have an overload for r-value references, that returns the result by-value, instead of by-reference.
That way the code below would work without issue and avoid any copies, as the result could simply be moved out of the container and still wouldn't need to be copied.

Example:

auto name = kdalgorithms::find_if(match.getAll("message-name"), [&range](const auto &name) {
        return range.contains(name);
    });

min_element() fails to compile on custom container without an empty() method

In my own class called Node, I wrote

	        using value_type     = std::unique_ptr<AbstractPrintItem>;
		using const_iterator = std::vector<std::unique_ptr<AbstractPrintItem>>::const_iterator;
		const_iterator begin() const
		{
			return m_printItems.begin();
		}
		const_iterator end() const
		{
			return m_printItems.end();
		}

This works fine for range-for, and STL algorithms.
But kdalgorithms::min_element failed to build with

kdalgorithms.h:214:19: error: ‘const class Node’ has no member named ‘empty’
  214 |     if (container.empty())

Shouldn't this be a begin()==end() test instead?

`copy` has to cope with self-copying

Can I concatenate a vector to itself using copy?

std::vector<int> v{1, 2, 3};
KDAlgorithms::copy(v, v); // duplicate it

The current answer is "maybe", which is a bad answer. If you want this to be a precondition, it should be documented (and assert()ed, e.g. assert(&input != &output)).

transform compile error in Visual Studio

I'm using transformed to convert a QVector into a QJsonArray of QJsonObjects as per the code below. It compiles OK with g++ 10 and clang++ 11, but not in Visual Studio 2022.

#include <kdalgorithms.h>

#include <QVector>
#include <QJsonArray>
#include <QJsonObject>

void f()
{
	struct S { int a, b; };
	QVector<S> s;

	auto j = kdalgorithms::transformed<QJsonArray>(s, [](const auto& v)
		{
			return QJsonObject{
				{ QStringLiteral("a"), v.a },
				{ QStringLiteral("b"), v.b }
			};
		});
}

The VS compiler reports

1>test.cpp(973,30): error C2039: 'a': is not a member of 'QJsonValue'
1>C:\qt\Qt5.15.12\5.15.12\msvc2019_64\include\QtCore\qjsonvalue.h(59,21): message : see declaration of 'QJsonValue'
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1470,1): message : see reference to function template instantiation 'auto f::<lambda_1>::operator ()<QJsonValue>(const _T1 &) const' being compiled
1>        with
1>        [
1>            _T1=QJsonValue
1>        ]
1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.34.31933\include\type_traits(1675,38): message : see reference to alias template instantiation 'std::_Decltype_invoke_nonzero<f::<lambda_1>,QJsonValue,>' being compiled
1>...\include\kdalgorithms\bits\transform.h(82,19): message : see reference to variable template 'const bool is_invocable_v<`f'::`2'::<lambda_1>,QJsonValue>' being compiled
1>test.cpp(974,30): error C2039: 'b': is not a member of 'QJsonValue'
1>C:\qt\Qt5.15.12\5.15.12\msvc2019_64\include\QtCore\qjsonvalue.h(59,21): message : see declaration of 'QJsonValue'
1>test.cpp(972,22): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'QJsonObject'
1>test.cpp(972,22): message : No constructor could take the source type, or constructor overload resolution was ambiguous

However if I replace the const auto& v parameter in the lambda with const S& v then it does compile.

It seems that with auto a QJsonValue is being passed, being the output container's value_type perhaps?

`remove` / `remove_if` are supposed to be spelled `erase`

Yeah, this is a hot take :-)

But Standard containers, Qt containers, etc. call the direct erasing operation erase, not remove:

// C++20
std::vector<int> v = ~~~;
erase(v, 42);

// Qt 6, C++17
QLits<int> list = ~~~;
erase(list, 42);

reserve_helper type conversion warning

I'm using kdalgorithms with Qt 5.15.12.

The following code generates warnings in Visual Studio about the type of size in reverse_helper.h:

	QVector<int> a{ 1,2,3 };
	auto b = kdalgorithms::filtered(a,
		[](const auto& i) { return i % 2; });
1>...\kdalgorithms\bits\reserve_helper.h(27,5): warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data
1>...kdalgorithms\bits\reserve_helper.h(36,5): message : see reference to function template instantiation 'bool kdalgorithms::detail::reserve_helper<Container>(Container &,size_t,std::true_type)' being compiled
1>        with
1>        [
1>            Container=QVector<int>
1>        ]

Do not use variadic type parameters in templates

A container's type may include non-type template parameters, e.g.

QVarLengthArray<int, 32> container;
                  // ^^ NTTP

Accepting them with template <typename ...> typename Container will therefore fail:

    QVector<int> vec{1, 2, 3, 4};
    const auto toString = [](int i) { return QString::number(i); };
    auto result = KDAlgorithms::transformed<QVarLengthArray>(vec, toString); // ERROR

For some reason this was a deliberate choice. But why simply using template <typename> typename Container doesn't work here?

Eradicate calls to .begin(), .end(), .cbegin(), .cend()

Because they don't work universally. Yes, it's SUPER annoying.

  • C arrays don't have .begin()
  • initializer_list, span, etc. don't have .cbegin()

Replace them by std::begin / std::cbegin, or even better, by centralized KDAlgorithm::detail::begin etc. functions that call

  • the std::ranges::begin() etc. functions, if ranges are available
  • the std::begin() etc. functions otherwise

In `remove` / `remove_if` consider C++20's uniform erasure protocol

If you have a std::list, you're supposed to call list.remove_if(predicate), and not std::remove_if(list, predicate).

That's because shuffling around the nodes of the list can be much much cheaper than shuffling around the contents of those nodes.

To cope with these differences, C++20 introduced free erase and erase_if functions, which I have also added to Qt containers.

remove/remove_if (or erase, cf #7 ) should therefore have some dispatching logic inside:

  • if you can call (through ADL!) erase(container, ...) or erase_if(container, ...) then do that
  • otherwise do the erase/remove idiom

gcc 13 warns about extra std::move() calls

kdalgorithms-src/src/bits/find_if.h:135:43: warning: moving a temporary object prevents copy elision [-Wpessimizing-move]
  135 |     return detail::create_result<Iterator>(std::move(it), std::move(range.begin()),
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  136 |                                            std::move(range.end()),
      |                                            ~~~~~~~~~~~~~~~~~~~~~~~
  137 |                                            std::is_lvalue_reference<Container>());
      |                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kdalgorithms-src/src/bits/find_if.h:135:43: note: remove ‘std::move’ call
kdalgorithms-src/src/bits/find_if.h:135:43: warning: moving a temporary object prevents copy elision [-Wpessimizing-move]
kdalgorithms-src/src/bits/find_if.h:135:43: note: remove ‘std::move’ call

The code is quite standard I think:

			const auto &objects = aMethodThatReturnsAConstRefToAQList();
			auto object = kdalgorithms::find_if(objects, [product](object) { return object->product() == product; });

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.