Home > Enterprise >  How to determine if one template argument are null?
How to determine if one template argument are null?

Time:08-15

I'm using this template to print different types of data to the console, however, if one argument is NULL it generates an exception.

How I can add verification to avoid getting an exception?

I tried adding:

 if (sizeof...(args) == 0)
    return;

But it still reads NULL values.

template <typename Arg, typename... Args>
void doPrint(Arg&& arg, Args&&... args)
{
    if (sizeof...(args) == 0)
        return;

    std::wostringstream out;

    out << std::forward<Arg>(arg);

    using expander = int[];
    (void)expander {
        0, (void(out << std::left << std::setw(20) << std::forward<Args>(args)), 0)...
    };

    OutputDebugString(out.str().c_str());
    std::cout << out.str().c_str() << std::endl;
}

How to reproduce the exception:

    BSTR name = NULL;
    doPrint("name: ", name);

CodePudding user response:

Issue is that operator << taking BSTR expects non-null pointer.

BSTR name = nullptr;
out << name; // Wrong expect non-null pointer

You might create overload for custom display and handling special case:

template <typename T>
std::wostream& doPrintImpl(std::wostream& out, const T& arg)
{
    return out << arg;
}

std::wostream& doPrintImpl(std::wostream& out, const BSTR arg)
{
    return out << (arg == nullptr ? L"NULL" : arg);
}


template <typename... Args>
void doPrint(const Args&... args)
{
    std::wostringstream out;

    auto print = [&](const auto& arg){
        doPrintImpl(out, arg);
        out << std::left << std::setw(20);
    };

    using expander = int[];
    (void)expander { 0, (print(args), 0)... }; // equivalent of C  17 (print(args), ...); 

    OutputDebugString(out.str().c_str());
    std::cout << out.str().c_str() << std::endl;
}

CodePudding user response:

First:

if (sizeof...(args) == 0)

This only check how many arguments you have. It doesn't do anything about what they actually are.

Instead you need to actually check if each arguments are nullptr. Naturally, you want something like:

args != nullptr
    ? out << std::left << std::setw(20) << std::forward<Args>(args)
    : out

This would work if all args are meant to be pointer types. However, it will fail if you pass in anything else, since operator!= isn't defined between value types and std::nullptr_t.

Instead, you could write a set of helper functions isNullptr, that will do either return false if you pass in a value type, or check if they are nullptr if you pass in a pointer type:

template <typename T>
bool isNullptr(T* ptr)
{
    return !ptr;
}

template <typename T>
bool isNullptr(T)
{
    return false;
}

Now, you can change your previous statement to:

!isNullptr(args)
    ? out << std::left << std::setw(20) << std::forward<Args>(args)
    : out

And you can put it in your expander:

(void)expander {((!isNullptr(args) ? out << std::left << std::setw(20) << std::forward<Args>(args) : out), 0)...};

Or for c 17 or later:

((!isNullptr(args) ? out << std::left << std::setw(20) << std::forward<Args>(args) : out), ...);
  • Related