-1

Let's say that I have a function which takes in pointers.

int functionA(int* a, int* b)
{
    ...
}

I can add null checks inside functionA. Is there a way that I can ensure that an error occurs on the compile time whenever nullptr is passed as a parameter.

Abhash Kumar Singh
  • 1,157
  • 2
  • 12
  • 22
  • 10
    Use references instead? – Alan Birtles Jan 23 '22 at 09:25
  • @StoryTeller-UnslanderMonica: yes ```nullptr``` or ```NULL```. @AlanBirtles: passing references will cause performance degradation because copy-constructors get involved at that point. – Abhash Kumar Singh Jan 23 '22 at 09:35
  • 4
    @AbhashKumarSingh *because copy-constructors get involved at that point.* -- What?? That's not how references work. Passing by value incurs copying, not passing by reference. – PaulMcKenzie Jan 23 '22 at 09:35
  • @PaulMcKenzie Let's say that input pointers are not int. It is a custom defined object. If I use references then every call to this function will involve call to copy-constructor of that class. – Abhash Kumar Singh Jan 23 '22 at 09:38
  • 5
    @AbhashKumarSingh -- Again, what?? Where are you learning C++? A reference literally takes the same object you're passing, and works with it. No copy construction is done. – PaulMcKenzie Jan 23 '22 at 09:38
  • @PaulMcKenzie I might be wrong. Let me run an experiment. To my knowledge pass by references also incur copy-constructors. – Abhash Kumar Singh Jan 23 '22 at 09:39
  • 3
    @AbhashKumarSingh, Passing by reference doesn't copy as long as the reference type matches. The only way to copy is to have a `const T&` parameter and pass it something other than a `T` so that a temporary `T` to bind to is created (which isn't a copy, but can effectively do the same thing depending on the types). A non-const `T&` will bind to the `T` object you pass in or else fail to compile. – chris Jan 23 '22 at 09:40
  • 1
    @AbhashKumarSingh And in any event, the compiler, if you used references or pointers, and the type is either `T *` or `T &`, will probably produce the same code. The only difference is in the C++ syntax that references and pointers use (`.` instead of `->`). – PaulMcKenzie Jan 23 '22 at 09:42
  • @all : my bad, copy constructor is called while returning the value(return by value). – Abhash Kumar Singh Jan 23 '22 at 09:52
  • 1
    @AbhashKumarSingh _"copy constructor is called while returning the value(return by value)"_ - That depends on what value you return. See RVO and NRVO. – Ted Lyngmo Jan 23 '22 at 10:01
  • 6
    Worth noting that, given enough rope, you can still hang yourself even with a reference if you work hard enough at dangling one. They're no silver bullet, but they will apply to what you're specifically trying to accomplish here (making compile-time nullptr detection moot by changing the function to not accept pointers in the first place). – WhozCraig Jan 23 '22 at 10:05
  • @all: Thanks lot I learned a lot of valuable concepts today. The thing is I need to fix a huge codebase which contains lots of segfaults due to null accesses. Hence I was looking for such a thing. Converting pointers to references is going to exponentially increase my job. Looks like I need to stick to adding `null` checks. – Abhash Kumar Singh Jan 23 '22 at 10:10
  • You could convert one function at a time to be taking references instead of pointers. I would start with the one that caused the latest segfault. If another function then causes segfault, convert that etc. – Ted Lyngmo Jan 23 '22 at 10:12
  • `an error occurs on the compile time whenever nullptr` How can you get a _compile-time_ error on runtime parameters? Or are argument guaranteed to be known at compile-time? – KamilCuk Jan 23 '22 at 10:24
  • @KamilCuk : Let's say that they are known. – Abhash Kumar Singh Jan 23 '22 at 10:35
  • Does https://stackoverflow.com/questions/59610746/how-to-get-a-compile-time-error-in-constant-evaluated-expression/59611383#59611383 answer your question? – KamilCuk Jan 23 '22 at 10:37
  • @KamilCuk: looks like it does. I need to understand "constant evaluated expression" to be sure. – Abhash Kumar Singh Jan 23 '22 at 10:42
  • This is unrelated to constexpr. `__builtin_constant_p` depends on the compiler optimizer, not on any C++ syntax. – KamilCuk Jan 23 '22 at 10:43

3 Answers3

2

Is there a way that I can ensure that an error occurs on the compile time whenever nullptr is passed as a parameter.

If you specifically mean the nullptr keyword, then sort of. You can provide overloads that would be chosen in that case, and define them deleted. This works as long as you don't explicitly bypass the overloading by casting for example.

int functionA(int*, std::nullptr_t) = delete;
int functionA(std::nullptr_t, int*) = delete;

// ...
functionA(&i, &j)            // OK
functionA(nullptr, &i);      // error
functionA(&i, nullptr);      // error
functionA(nullptr, nullptr); // error

or NULL

This will require adding overloads for integers in addition to the previous overloads:

int functionA(int*, int) = delete;
int functionA(int, int*) = delete;

// ...
functionA(NULL, &i);      // error
functionA(&i, NULL);      // error
functionA(NULL, NULL);    // error

If you mean any pointer with null value, then that cannot be done because the values of function arguments cannot generally be known at compile time.

If your goal is to not use the pointers as iterators, then it would be safer and more convenient to pass references instead.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

The correct thing to do here is use references.

int functionA(int& a, int& b);

The mental model to have about references is: these are pointers that should never represent nullptr.

JVApen
  • 11,008
  • 5
  • 31
  • 67
  • 1
    What if the function body is `for (; a != b; ++a) ...`? Pointers are not only indirection, but they are also iterators. Not all non-null pointers can be replaced by references. – eerorika Jan 23 '22 at 12:20
  • 1
    @eerorika while i 100% agree with you, you can still get the address of a reference by simply dereferencing the reference the same way you would a normal variable. `&variable`. simply setting the value to a `uintptr_t` allows you to do that procedure pretty much the same. – Ty Q. Jan 23 '22 at 12:31
1

I've somehow managed to find a way to achieve this after a good hour of Google searching. If you really really want to do compile time checking, this is how you can achieve it.

#include <iostream>
#include <cstdlib>

template<typename T, T* N>
static void ValidatePtr() {
    std::static_assert(N != nullptr);
}

int main() {
    constexpr int* ptr = nullptr;
    ValidatePtr<int, ptr>();

    std::cout << "Test" << std::endl;
}

Quick rundown: If you need to validate compile-time pointers for functions, you're going to have to make sure they're constant expressions. The method will actually require a constant expression to even compile. This ensures that the only pointers passed will have to be determined at compile time. You cannot reference the address of other variables using &variable as it will throw compiler errors, so it has to be specifically typed out like constexpr int* ptr = nullptr; or constexpr int* ptr = (int*)0xFFFFFFFF.

However, if you are wanting dynamic variables to be checked, you need to check at runtime since there is no way without using constexpr to determine what the address will be.

I don't really see a use of this outside of maybe a memory utility.

I heavily just recommend having a runtime check for a nullptr and if you feel it is necessary, use a normal assert call instead of a static_assert or simply raise an exception using throw.

Ty Q.
  • 207
  • 2
  • 13