GithubHelp home page GithubHelp logo

kdalgorithms's People

Contributors

aambrosano avatar daniel-petrovic avatar dantti avatar dfaure-kdab avatar jesperkdab avatar narnaud avatar phybrackets avatar pre-commit-ci[bot] avatar seanharmer avatar winterz 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 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  avatar  avatar

kdalgorithms's Issues

`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);

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?

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);
    });

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

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>        ]

`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)).

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?

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)

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?

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; });

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.

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

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.