This would be nice:
void processOptions(StringPairArray options)
{
for (auto& [key, val] : options)
{
applyOption(key, val);
}
}
This would be nice:
void processOptions(StringPairArray options)
{
for (auto& [key, val] : options)
{
applyOption(key, val);
}
}
This is new to me, but I tried the following, based on this post but I get a this range-based 'for' statement requires a suitable "begin" function and none was found
.
template<>
struct std::tuple_size<StringPairArray>
: std::integral_constant<std::size_t, 2> {};
template<> struct std::tuple_element<0, StringPairArray> { using type = StringRef; };
template<> struct std::tuple_element<1, StringPairArray> { using type = StringRef; };
So I’m going with:
for (auto& key : options.getAllKeys())
{
auto& val = options[key];
applyOption(key, val);
}
The problem is that juce::StringPairArray
does not have a begin
and end
member function. In cases like this you can still enable range-based-for loop support if you supply begin
and end
functions as free functions. These functions should return a suitable iterator class. A juce::StringPairArray
stores keys and values in two individual juce::StringArray
members, so this custom iterator class needs to hold iterators to both arrays, increase both iterators simultaneously in operator++
. Dereferencing that iterator should return a struct with two references to the current key and value. If a simple struct like that is returned, we don’t even need to do anything special to enable structured bindings.
I just whipped up a quick proof of concept implementation like this:
// Forward declarations
class StringPairIterator;
namespace juce
{
StringPairIterator begin (const juce::StringPairArray& spa);
StringPairIterator end (const juce::StringPairArray& spa);
}
struct StringPairRef
{
const juce::String& key;
const juce::String& value;
};
class StringPairIterator
{
public:
StringPairIterator& operator++()
{
++key;
++value;
return *this;
}
StringPairRef operator*()
{
return { *key, *value };
}
bool operator== (const StringPairIterator& other) const
{
return key == other.key;
}
private:
friend StringPairIterator juce::begin (const juce::StringPairArray&);
friend StringPairIterator juce::end (const juce::StringPairArray&);
StringPairIterator (const juce::String* k, const juce::String* v)
: key (k),
value (v)
{}
const juce::String* key;
const juce::String* value;
};
namespace juce
{
StringPairIterator begin (const juce::StringPairArray& spa)
{
return { spa.getAllKeys().begin(), spa.getAllValues().begin() };
}
StringPairIterator end (const juce::StringPairArray& spa)
{
return { spa.getAllKeys().end(), spa.getAllValues().end() };
}
}
With that, this works fine for me:
void processOptions (juce::StringPairArray options)
{
for (const auto& [key, val] : options)
{
applyOption (key, val);
}
}
One note: Since StringPairArray
is implemented in the juce
namespace, the begin
and end
functions have to be implemented in the same namespace to make the lookup of the correct overload work.
Thank you, Sir Penguin! Most educational.