Blueeyes789
Blueeyes789

Reputation: 573

Check if only one string variable is not nullptr in C++

I have three LPCWSTR string variables called A, B, C.

I am assigning them from another function which can sometimes return nullptr if something goes wrong. like this:

A = MyFunc();
B = MyFunc();
C = MyFunc();

Now, for some stuff with those variables, I need to check if only one of these variables is not nullptr(only one of variables is assigned).

I tried to do this myself like:

if ((A == nullptr) && (B == nullptr) && (C <> nullptr)) {}

Any ideas about how to do this are welcome.

Upvotes: 12

Views: 3202

Answers (5)

paxdiablo
paxdiablo

Reputation: 881113

Easy enough to do with:

int numSet = 0;
A = MyFunc(); if (A != nullptr) numSet++;
B = MyFunc(); if (B != nullptr) numSet++;
C = MyFunc(); if (C != nullptr) numSet++;
if (numSet == 1) // only one is set

You could also encapsulate the behaviour with a helper function:

LPCWSTR MyFuncWithCount(int &countSetProperly) {
    LPCWSTR retVal = MyFunc();
    if (retVal != nullptr) countSetProperly++;
    return retVal;
}

int numSet = 0;
A = MyFuncWithCount(numSet);
B = MyFuncWithCount(numSet);
C = MyFuncWithCount(numSet);
if (numSet == 1) // only one is set

Next step up from there would be using a range-based for loop in conjunction with a braced init list, as per the following complete program:

#include <iostream>
#include <vector>

typedef void * LPCWSTR;  // Couldn't be bothered including Windows stuff :-)

int main() {
    // Only set two for test purposes.

    LPCWSTR A = nullptr, B = nullptr, C = nullptr;
    LPCWSTR D = &A,      E = nullptr, F = &A;

    int numSet = 0;
    for (const auto &pointer: {A, B, C, D, E, F})
        if (pointer != nullptr)
            numSet++;

    std::cout << "Count is " << numSet << std::endl;
}

Or you could embrace modern C++ in all its glory by using lambda functions, as per the following:

#include <iostream>
#include <vector>

typedef void * LPCWSTR;  // Couldn't be bothered including Windows stuff :-)

int main() {
    // Only set two for test purposes.

    LPCWSTR A = nullptr, B = nullptr, C = nullptr;
    LPCWSTR D = &A,      E = nullptr, F = &A;

    int numSet = 0;
    [&numSet](const std::vector<LPCWSTR> &pointers) {
        for (const auto &pointer: pointers)
            if (pointer != nullptr)
                numSet++;
    } (std::vector<LPCWSTR>{A,B,C,D,E,F});

    std::cout << "Count is " << numSet << std::endl;
}

That's probably overkill for your particular case however :-)

Upvotes: 15

templatetypedef
templatetypedef

Reputation: 372664

I'm not a huge fan of using techniques like the following in general, but you can use the fact that for any pointer ptr that !!ptr evaluates to 0 for a null pointer and 1 for a non-null pointer to write

if (!!A + !!B + !!C == 1) {
    ...
}

as a dense way to get this to work. It's essentially the same as @Davislor's solution but with a more compact "test if null" check.

This approach doesn't scale nearly as well as the accepted approach does, and it's trickier to read, but depending on your audience and who's reading the code it might do the trick nicely.

Upvotes: 3

Davislor
Davislor

Reputation: 15134

In C++, for backward-compatibility with C, the return value of a relational operator is an int equal to 0 or 1. So you can do:

if ( (a != nullptr) + (b != nullptr) + (c != nullptr) == 1 )

If you want to use logical operators only as logical operators, there are also disjunctive normal form and conjunctive normal form, albeit with more operations.

if ( (a && !b && !c) || (!a && b && !c) || (!a && !b && c) )

 

if ( (a || b || c) && (!a || !b) && (!a || !c) && (!b || !c) )

The former is not difficult to read in this simple case, compared to most of the other solutions, although it would quickly get too verbose if there were more possible solutions.

You can also stick them in any container, such as a std::array<LPCWSTR, 3>, and do std::count( pointers.begin(), pointers.end(), nullptr) (as Jarod42 suggested).

Upvotes: 4

Jarod42
Jarod42

Reputation: 217075

With std, you may do:

const auto vars = {A, B, C}; // Create initializer list.
const bool onlyOneNotNull =
    (std::count(vars.begin(), vars.end(), nullptr) == (vars.size() - 1);
// then you may use find_if to retrieve the non null variable.

Upvotes: 11

Jesper Juhl
Jesper Juhl

Reputation: 31447

Here's one simple way:

int not_null = 0;
not_null += A != nullptr;
not_null += B != nullptr;
not_null += C != nullptr;
if (not_null == 1) {
    /* Do stuff */
}

Check each for being nullptr and increment a count if it is not. If the count comes out as 1 in the end, do your thing.

Upvotes: 7

Related Questions