C ++ 17 structured binding which also includes an existing variable

This SO answer contains some of the disadvantages of C ++ 17 decomposition declarations (a feature formerly known as "structured binding"). For example, you cannot give explicit types to new variables, and so on. But one big flaw I am facing is not mentioned here, so I wonder if there is a known workaround that I just don't think of.

Consider this JSON-parsing code (which may contain other errors, please ignore them for the purpose of this question):

using Value = std::any;
using String = std::string;
using Object = std::map<String, Value>;

std::pair<String, const char *> load_string(const char *p, const char *end);
std::pair<Value, const char *> load_value(const char *p, const char *end);
const char *skip_spaces(const char *p, const char *end);

std::pair<Object, const char *> load_object(const char *p, const char *end)
{
    p = skip_spaces(p, end);
    if (p == end || *p++ != '{') throw ParseError("Expected {");
    p = skip_spaces(p, end);
    Object result;
    if (p == end && *p == '}') {
        // the object has no key-value pairs at all
    } else {
        while (true) {
            auto [key, p] = load_string(p, end);
            p = skip_spaces(p, end);
            if (p == end || *p++ != ':') throw ParseError("Expected :");
            auto [value, p] = load_value(p, end);
            result.insert_or_assign(std::move(key), std::move(value));
            p = skip_spaces(p, end);
            if (p == end) throw ParseError("Expected , or }");
            if (*p == '}') break;
            if (*p++ != ',') throw ParseError("Expected , or }");
        }
    }
    return {result, p+1};
}

      

This will work just fine, except that lines that start with auto [key, p] =

and auto [value, p] =

are invalid! The variable p

has already been declared. I'm trying to assign a p

new value, but I don't want to create a whole new local variable.

I would rather not use std::tie(key, p) =

it because it requires me to advertise for key

before assigning. This is a familiar objection to std::tie

. Which I could swear to have structured binding introduced into the language!

So, is there any workaround - any good clean way of writing a combined construct - key

-in-place-and-also-assign-to- p

that expresses my intent?

It's weird how I've never missed this feature before, but once you give me a structured binding for the game, the first thing I try doesn't work. :(

+6


source to share


1 answer


This is a really stupid idea that I wouldn't seriously speculate about unless it ends up being a sensible workaround ... but consider the following code.

template<size_t P, size_t... Is>
auto plus(std::index_sequence<Is...>)
{
    return std::index_sequence<P+Is...>{};
}

template<typename RHS, size_t... Is>
auto tuple_select(RHS&& rhs, std::index_sequence<Is...>)
{
    return std::forward_as_tuple(std::get<Is>(std::forward<RHS>(rhs))...);
}

template<typename... Ts>
struct AndTie {
    std::tuple<Ts&...> v;
    AndTie(Ts&... vs) : v(vs...) {}

    template<typename RHS>
    auto operator=(RHS&& rhs) && {
        constexpr int N = std::tuple_size_v<RHS>;
        constexpr int K = sizeof...(Ts);
        v = tuple_select(std::forward<RHS>(rhs), plus<N-K>(std::make_index_sequence<K>{}));
        return tuple_select(std::forward<RHS>(rhs), std::make_index_sequence<N-K>{});
    }
};

      

This gives us



auto [key] =AndTie(p)= load_string(p, end);
auto [value] =AndTie(p)= load_value(p, end);

      

It still has the limitation that "bound" lvalues ​​are constrained to appear last, and "declared" variables are constrained to appear first, but I don't think there are many ways around this. And something like tuple_shuffle<Is...>

can handle it if you need it.

0


source







All Articles