jma
jma

Reputation: 3799

PostgreSQL C++ (pqxx) and setting value if not NULL

I have some code that looks like this:

for (const auto& query_result_row : query_result) {
    my_struct.a = query_results_row["a"].as<int>();
    my_struct.b = query_results_row["b"].as<string>();
    // and so forth.
}

In general, there might be quite a number of fields of different types. And that's good, but if a selected field is NULL, this will throw an exception. So instead I've now written this, which is highly awkward and more than a bit error-prone:

for (const auto& query_result_row : query_result) {
    if (!query_results_row["a"].is_null()) {
        my_struct.a = query_results_row["a"].as<int>();
    }
    if (!query_results_row["ab"].is_null()) {
        my_struct.b = query_results_row["b"].as<string>();
    }
    // and so forth.
}

Ick.

Perhaps, I thought, I could make a (templated) function that simplifies this:

for (const auto& query_result_row : query_result) {
    MaybeSet(my_struct.a, query_results_row["a"]);
    MaybeSet(my_struct.b, query_results_row["b"]);
    // and so forth.
}

The problem here is that query_results_row["a"] is itself a templated type, and, moreover, the as() type may not be precisely the same as the my_struct type (e.g., different types of ints) and although I don't see it today, that smells like the sort of thing that some day could lead to an unintended cast.

// Doesn't work.
template <typename valueT, typename postgresFieldT>
void MaybeSet(valueT& my_struct_field, const postgresFieldT& field) {
    if (!field.is_null()) {
        my_struct_field = field.as<valueT>();
    }
}

Any suggestions on a cleaner way to express this idea of setting things if they're not not null but not trying if they are?

Upvotes: 1

Views: 1758

Answers (1)

jma
jma

Reputation: 3799

For future generations: I slightly changed the problem and so arrived at a simple solution.

template <typename T>
class SetThing {
    // ...
    void MaybeSet(const pqxx::result::field& field) {
        if (!field.is_null()) {
            SetClean(field.as<T>());
        }
    }
    template <typename asT>
    void MaybeSet(const pqxx::result::field& field) {
        if (!field.is_null()) {
            SetClean(field.as<asT>());
        }
    }
    // ...
}

Usage then becomes

SetThing thing;
thing.MaybeSet(query_result_row["dog"]);
thing.MaybeSet<the_cast_type>(query_result_row["complicated"]);

The second form is used for a handful of complex types (e.g., classes) that I can construct from something simple (say, an int or a string) but which isn't the base (templated) type of the SetThing.

Upvotes: 2

Related Questions