Hi,
As requested by @dkazakov in the last meeting, I'm filing this one to gauge support for a change to our code style.
The change in question I am proposing is to stop using std::bind in new code, and remove the existing (~230) instances from the codebase before they increase due to the impending Lager rewrite.
Rationale
From a general point of view, std::bind is one of the discouraged language features by clang-tidy 1:
std::bind can be hard to read and can result in larger object files and binaries due to type information that will not be produced by equivalent lambdas.
For example, when debugging the PSD parser for bug 464700, the use of bind for the XML callbacks resulted in a stacktrace like this, where the actual function that's calling the pattern finder is invisible, inside the innards of std::function:
kritaimage.dll!KisAslLayerStyleSerializer::assignPatternObject(const QString & patternUuid, const QString & patternName, std::function<void __cdecl(QSharedPointer<KoPattern>)> setPattern) Line 949 (e:\krita-win\src\libs\image\kis_asl_layer_style_serializer.cpp:949) [Inline Frame] kritaimage.dll!std::invoke(void(KisAslLayerStyleSerializer::*)(const QString &, const QString &, std::function<void __cdecl(QSharedPointer<KoPattern>)>) &) Line 1580 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\type_traits:1580) [Inline Frame] kritaimage.dll!std::_Invoker_ret<std::_Unforced>::_Call(void(KisAslLayerStyleSerializer::*)(const QString &, const QString &, std::function<void __cdecl(QSharedPointer<KoPattern>)>) &) Line 683 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\functional:683) [Inline Frame] kritaimage.dll!std::_Call_binder(std::_Invoker_ret<std::_Unforced>) Line 1975 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\functional:1975) [Inline Frame] kritaimage.dll!std::_Binder<std::_Unforced,void (__cdecl KisAslLayerStyleSerializer::*)(QString const &,QString const &,std::function<void __cdecl(QSharedPointer<KoPattern>)>),KisAslLayerStyleSerializer *,std::_Ph<1> const &,std::_Ph<2> const &,std::function<void __cdecl(QSharedPointer<KoPattern>)> &>::operator()(const QString &) Line 2012 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\functional:2012) [Inline Frame] kritaimage.dll!std::invoke(std::_Binder<std::_Unforced,void (__cdecl KisAslLayerStyleSerializer::*)(QString const &,QString const &,std::function<void __cdecl(QSharedPointer<KoPattern>)>),KisAslLayerStyleSerializer *,std::_Ph<1> const &,std::_Ph<2> const &,std::function<void __cdecl(QSharedPointer<KoPattern>)> &> &) Line 1574 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\type_traits:1574) [Inline Frame] kritaimage.dll!std::_Invoker_ret<void>::_Call(std::_Binder<std::_Unforced,void (__cdecl KisAslLayerStyleSerializer::*)(QString const &,QString const &,std::function<void __cdecl(QSharedPointer<KoPattern>)>),KisAslLayerStyleSerializer *,std::_Ph<1> const &,std::_Ph<2> const &,std::function<void __cdecl(QSharedPointer<KoPattern>)> &> &) Line 670 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\functional:670) kritaimage.dll!std::_Func_impl_no_alloc<std::_Binder<std::_Unforced,void (__cdecl KisAslLayerStyleSerializer::*)(QString const &,QString const &,std::function<void __cdecl(QSharedPointer<KoPattern>)>),KisAslLayerStyleSerializer *,std::_Ph<1> const &,std::_Ph<2> const &,std::function<void __cdecl(QSharedPointer<KoPattern>)> &>,void,QString const &,QString const &>::_Do_call(const QString & <_Args_0>, const QString & <_Args_1>) Line 834 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\functional:834) [Inline Frame] kritapsdutils.dll!std::_Func_class<void,QString const &,QString const &>::operator()(const QString &) Line 874 (c:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32213\include\functional:874) kritapsdutils.dll!KisAslCallbackObjectCatcher::addPatternRef(const QString & path, const QString & patternUuid, const QString & patternName) Line 178 (e:\krita-win\src\libs\psdutils\asl\kis_asl_callback_object_catcher.cpp:178)
Scott Meyers in his book "Effective Modern C++" (see item 34) has also recommended its outright removal from C++14 onwards:
This history means that some programmers have a decade or more of experience using std::bind. If you’re one of them, you may be reluctant to abandon a tool that’s served you well. That’s understandable, but in this case, change is good, because in C++11, lambdas are almost always a better choice than std::bind. As of C++14, the case for lambdas isn’t just stronger, it’s downright ironclad.
He identifies three issues with std::bind:
- inability to automatically choose overloads based on function parameter types
- inability to go beyond simple parameter type binding
- the parameter passing is by value, never by reference or capture
- the parameter placeholding is completely opaque to the developer
Uses
We use std::bind in three places:
- the vast majority (134 through macro, 41 directly) as callbacks for the ASL parser
- 9 as callbacks for KisSignalCompressorWithParam
- the remaining as either isolated instances of std::function or a way to implicitly cast values between types