12

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.

11
  • 1
    Can't you chain xor, then negate the result? Something like !(A ^ B ^ C). ^ is bitwise, but it may work? Just spit balling. Commented Aug 4, 2017 at 4:21
  • 1
    Can we assume you do not care which one is null so long as one and only one is null? Commented Aug 4, 2017 at 4:27
  • 3
    Unless you want to be cursed for eternity by other programmers reading your code, I'd steer clear of the "clever" solutions like XOR :-) Optimise for readability first! Commented Aug 4, 2017 at 4:30
  • 6
    C <> nullptr is not valid C++. What you tried to express there is likely C != nullptr. Commented Aug 4, 2017 at 4:42
  • 4
    For only 3 variables, the simplest solution really is (A && !B && !C) || (!A && B && !C) || (!A && !B && C) - don't get sucked into abstraction! (Or !!A + !!B + !!C == 1) Commented Aug 4, 2017 at 10:27

5 Answers 5

15

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 :-)

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! This woked fine. However, I will also take a look at XORs. :-)
Couldn't you use std::initializer_list rather than std::vector to avoid allocations. That and... std::count.
I have never seen an immediately-invoked lambda expression in C++ before... why not just for (const auto& pointer : {A, B, C, D, E, F}) { ...?
Your coding style is... upsetting.
@Tavian et al, it wasn't really a serious suggestion, I was just progressing from basic to uber-advanced. However, I may just slot your suggestion in between helper-function and ridiculous-lambda :-)
11

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.

Comments

7

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.

Comments

4

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).

Comments

3

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.

1 Comment

You’d win if this were Code Golf! I don’t think I’d actually write it that way, but it’s clever.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.