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.

Scath
  • 3,777
  • 10
  • 29
  • 40
Blueeyes789
  • 543
  • 6
  • 18
  • 1
    Can't you chain `xor`, then negate the result? Something like `!(A ^ B ^ C)`. `^` is bitwise, but it may work? Just spit balling. – Carcigenicate Aug 04 '17 at 04:21
  • @Carcigenicate Thanks! But I never used `xor` s in C++ :-( Will try it anyway. – Blueeyes789 Aug 04 '17 at 04:23
  • 1
    Can we assume you do not care which one is null so long as one and only one is null? – user4581301 Aug 04 '17 at 04:27
  • @user4581301 actually. You're right. I want to know if only one variables from these are assigned. – Blueeyes789 Aug 04 '17 at 04:28
  • 1
    Whoops, actually, I think to use XOR you'd have to negate each of them individually, *then* apply XOR. All of the online C++ compilers are horribly broken for mobiles, so I can't test it. – Carcigenicate Aug 04 '17 at 04:29
  • 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!* – paxdiablo Aug 04 '17 at 04:30
  • @paxdiablo do you mean this also possible to do by using XOR? – Blueeyes789 Aug 04 '17 at 04:31
  • 1
    @paxdiablo In retrospect, the answer below *are* more readable. This just screamed "exclusive" when I first read it, so XOR jumped to mind. – Carcigenicate Aug 04 '17 at 04:32
  • 6
    `C <> nullptr` is not valid C++. What you tried to express there is likely `C != nullptr`. – Jesper Juhl Aug 04 '17 at 04: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`) – user253751 Aug 04 '17 at 10:27
  • Side note: When C++ 17 is more prevalent, consider migrating your code to return an optional http://en.cppreference.com/w/cpp/utility/optional – Alexander Aug 04 '17 at 18:17

5 Answers5

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

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Thanks! This woked fine. However, I will also take a look at XORs. :-) – Blueeyes789 Aug 04 '17 at 04:34
  • Couldn't you use `std::initializer_list` rather than `std::vector` to avoid allocations. That and... `std::count`. – Matthieu M. Aug 04 '17 at 13:20
  • 4
    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}) { ...`? – Tavian Barnes Aug 04 '17 at 13:55
  • 2
    Your coding style is... upsetting. – Krupip Aug 04 '17 at 15:10
  • @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 :-) – paxdiablo Aug 05 '17 at 01:58
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.
Jarod42
  • 203,559
  • 14
  • 181
  • 302
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.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
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).

Davislor
  • 14,674
  • 2
  • 34
  • 49
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.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • You’d win if this were Code Golf! I don’t think I’d actually write it that way, but it’s clever. – Davislor Aug 04 '17 at 19:59