banner
cells

cells

为美好的世界献上 bug

Namespace Pitfalls

Namespaces & Source Files#

Consider the following code:

// current_thread.h
#ifndef _CURRENT_THREAD_H_
#define _CURRENT_THREAD_H_

namespace mymuduolib {

namespace currentthread {

extern __thread int t_cached_tid;

void cachedTid();

} // namespace currentthread

} // namespace mymuduolib

#endif // _CURRENT_THREAD_H_

To implement the definitions in the source file, you can add the mymuduolib::currentthread:: prefix before the implementation names to specify the namespace:

// current_thread.cpp
#include "current_thread.h"
#include <unistd.h>
#include <sys/syscall.h>

__thread int mymuduolib::currentthread::t_cached_tid = 0;

void mymuduo::currentthread::cachedTid() {
    if (t_cached_tid == 0) {
        // get the current id through Linux system call
        t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
    }
}

If the namespace prefix is not added, there will be two t_cached_tid and cachedTid() definitions, one in the mymuduo::currentthread:: namespace and another in the :: namespace. The definition in the mymuduo::currentthread:: namespace is not implemented. If using namespace mymuduolib::currentthread; is used in a module, it will call the cachedTid() function in the mymuduolib::currentthread:: namespace. (Of course, this code will not compile without adding the namespace prefix because the variable t_cached_tid in the function will have an ambiguous specification).

Alternatively, you can place the implementation code in the same namespace as the header file:

// current_thread.cpp
#include "current_thread.h"
#include <unistd.h>
#include <sys/syscall.h>

namespace mymuduolib {

namespace currentthread {

__thread int t_cached_tid = 0;

void cachedTid() {
    if (t_cached_tid == 0) {
        // get the current id through Linux system call
        t_cached_tid = static_cast<pid_t>(::syscall(SYS_gettid));
    }
}
    
} // namespace mymuduolib
    
} // namespace currentthread

However, in this case, you cannot simply use using namespace mymuduolib::currentthread; because the scope of the function and variable implementations needs to be explicitly specified. If not specified, it means that the subsequent implementation content is independent of that namespace.

But for member implementations in a class, you can use the using namespace namespace:

// poller.h
#ifndef _POLLER_H_
#define _POLLER_H_

namespace mymuduolib {

namespace net {

class Poller {
    Poller();
};

}

} // namespace net

#endif // _POLLER_H_

In the source file:

// poller.cpp
#include "poller.h"

// mymuduolib::net::Poller::Poller() {}

using namespace mymuduolib::net;

Poller::Poller() {}

Namespaces & Forward Declarations#

Forward declarations can avoid circular dependency issues, but there is a common pitfall when encountering namespaces with forward declarations.

Consider the following erroneous code:

// addr.h
#ifndef _ADDR_H_
#define _ADDR_H_

namespace net {

class Addr {};

} // namespace net

#endif // _ADDR_H_
// http_connect.h
#ifndef _HTTP_CONNECT_H_
#define _HTTP_CONNECT_H_

class Addr;

namespace net {

class HttpConnect {
public:
    HttpConnect(const Addr &);
};

} // namespace net

#endif // _HTTP_CONNECT_H_
// http_connect.cpp
#include "http_connect.h"
#include "addr.h"

using namespace net;

HttpConnect::HttpConnect(const Addr &) {}
// main.cpp
#include "http_connect.h"

int main() {
    net::HttpConnect(Addr());
}

Error message:

http_connect.cpp:6:1: error: no declaration matches ‘net::HttpConnect::HttpConnect(const net::Addr&)
    6 | HttpConnect::HttpConnect(const Addr &) {}
      | ^~~~~~~~~~~
In file included from http_connect.cpp:1:
http_connect.h:8:7: note: candidate is: ‘constexpr net::HttpConnect::HttpConnect(net::HttpConnect&&)
    8 | class HttpConnect {
      |       ^~~~~~~~~~~
http_connect.h:8:7: note:         ‘constexpr net::HttpConnect::HttpConnect(const net::HttpConnect&)
http_connect.h:10:5: note:         ‘net::HttpConnect::HttpConnect(const Addr&)
   10 |     HttpConnect(const Addr &);
      |     ^~~~~~~~~~~
http_connect.h:8:7: note: ‘class net::HttpConnect’ defined here
    8 | class HttpConnect {
      |       ^~~~~~~~~~~

You might think that this error is caused by the using namespace, but it is not. Even using net::HttpConnect::HttpConnect(const net::Addr&) will result in the same error.

The issue here is actually with the forward declaration. We need to place the forward declaration class Addr inside the namespace net because the forward declaration class Addr does not exist in the :: scope.

// http_connect.h
#ifndef _HTTP_CONNECT_H_
#define _HTTP_CONNECT_H_

namespace net {

class Addr;

class HttpConnect {
public:
    HttpConnect(const Addr &);
};

} // namespace net

#endif // _HTTP_CONNECT_H_

If there are any mistakes, please let me know.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.