I need to compile the same code with two different libraries. One allows the objects to be const, the other doesn't. The solution that's implemented now looks like this:
#ifdef (OLDLIB)
ClassFoo TheFoo = Bar();
#else
const ClassFoo TheFoo = Bar();
#endif
This is used many times for different classes and impedes readability. I'd like to make the distinction somewhere else.
I guess one could say:
#ifdef (OLDLIB)
#define CLASS_FOO ClassFoo
#define CLASS_BAR ClassBar
#else
#define CLASS_FOO const ClassFoo
#define CLASS_BAR const ClassBar
#endif
CLASS_FOO TheFoo = Bar();
CLASS_BAR TheBar = FooBar();
But I dislike preprocessor stuff. Is there a nice C++-Way of doing the above? Thanks.
Update 1:
As Peter Wood said, it is possible to just instantiate them non-const. I changed the sentence.
You can use std::conditional to select between two types based on a compile-time variable:
#ifdef OLDLIB
constexpr bool OLD = true;
#else
constexpr bool OLD = false;
#endif
std::conditional<OLD, ClassFoo, const ClassFoo>::type theFoo;
~~~ ~~~~~~~~ ~~~~~~~~~~~~~~
true false
Since C++11
I'm pretty sure you're stuck with the preprocessor to accomplish your goal.
However, I'd probably write it like this:
#ifdef OLDLIB
# define LIB_CONST
#else
# define LIB_CONST const
LIB_CONST ClassFoo TheFoo(Bar());
Either way is not super elegant but doing it this way at least means that you're only tweaking an object attribute via the preprocessor rather than the whole object declaration.
The easiest solution seems to be to just use non-const objects and let the compiler automatically add const where the new interface expects it.
Alternately could you use a typedef inside the proprocessor block?
#ifdef (OLDLIB)
typedef ClassFoo InstantiableFoo;
#else
typedef const ClassFoo InstantiableFoo;
#endif
You can typedef
// oldlib.h
typedef ClassFoo Foo;
typedef ClassBar Bar;
// newlib.h
typedef const ClassFoo Foo;
typedef const ClassBar Bar;
// the library's user
#include "oldlib.h" // or include "which_lib.h" that includes
// one of oldlib.h newlib.h
#include "your_library_that_uses_the_typedefs.h"
Foo TheFoo = Bar();
Bar TheBar = FooBar();
You can parametrize your library's classes and global functions
// library.h
template <class Foo>
class SomeClass { }
template <class Foo>
Foo makeFoo() { }
// library_user.cpp
#include "library.h"
SomeClass<const ClassFoo> sc;
sc.method();
const ClassFoo f = makeFoo();
You can even hide the type Foo inside of the external libraries
// external_library.h
class ExternalLibrary {
typedef const Foo foo_type;
};
ExternalLibrary::foo_type& foo_identity(const ExternalLibrary::foo_type &v) { return v; }
// your_library.h
ExternalLibrary::foo_type makeFoo() { }
foo_identity(f1);
Related
For simplicity, I will omit things like proper typedefs to opaque structs instead of void *, Windows calling conventions, fixed integer types, etc.
Suppose I have the following files:
CApi.h -- Shared library header with C linkage for portability and hiding proprietary code.
#define LIB_API // library import/export details
extern "C" {
typedef int error;
error LIB_API lib_foo_create(void ** foo);
error LIB_API lib_foo_destroy(void * foo);
error LIB_API lib_foo_func(void * foo, int * out);
error LIB_API lib_bar_create(void ** bar);
error LIB_API lib_bar_destroy(void * bar);
error LIB_API lib_bar_func(void * bar, int * out); // Suppose this internally uses Foo::func from CxxApi.hpp with C++17
} // extern C
CxxApi.hpp -- Header only wrapper to simplify API usage.
#include "CApi.h"
namespace lib {
namespace detail {
template < typename Return = void, typename Func, typename... Args >
Return c_api(Func func)
{
// Not sure how this affects ODR if client and provider code use different versions,
// but neither see each other's code usage.
#if __cplusplus >= 201703L // C++17
// more efficient implmentation (e.g. fold expressions)
#else
// fallback implmentation (e.g. recursion)
#endif
}
} // namespace lib::detail
class Foo
{
public:
Foo() { detail::c_api(lib_foo_create, &handle_); }
~Foo() { detail::c_api(lib_foo_destroy, handle_); }
int func() { return detail::c_api<int>(lib_foo_func, handle_); }
private:
void * handle_;
};
struct Bar
{
public:
Bar() { detail::c_api(lib_bar_create, &handle_); }
~Bar() { detail::c_api(lib_bar_destroy, handle_); }
int func() { return detail::c_api<int>(lib_bar_func, handle_); }
private:
void * handle_;
};
} // namespace lib
If I, the API provider, compile this using C++17, but the client using the API uses C++11, is ODR violated with lib::detail::c_api?
I believe it is not because lib_bar_func's definition is in a different translation unit than client code, but I am not positive.
I have some well-formed code looks like this:
NAMESPACE_BEGIN(Foo)
inline void test() {
string s = xxx;
}
NAMESPACE_END(Foo)
So, is there any portable tricks by using the NAMESPACE_BEGIN() macro to obtain namespace name "Foo" in test()?
I'm thinking of something like this, but it would surely cause symbol redefinition:
#define NAMESPACE_BEGIN(x) \
namespace x { \
inline const char *_curNamespace() { \
return #x; \
}
#define NAMESPACE_END(x) \
}
There's also a workaround looks like this, but that's not very convenient
#define NAMESPACE_NAME Foo
// using a header file so that we can use #ifdef guard
#include "MyNamespaceBegin.h"
...
#include "MyNamespaceEnd.h"
EDIT:
Why I need this:
I'm using much of macro to generate codes to achieve some
dynamic reflection logic (yes, not static template reflection),
it's all right within class scope by using static member function,
but does not work for namespaces
Why not to manually declare the name getter once:
What I want is something like this:
// the global default version
const char *_curNamespace() {return "";}
namespace X {
// the local namespace version
const char *_curNamespace() {return "X";}
// some verbose reflection register code
...
registerSomething(_curNamespace());
...
}
Of course, all of the verbose register code should be generated by macro
And, app level user should not care about the _curNamespace(),
so, I want to simplify the user's usage,
by using a custom NAMESPACE_BEGIN(xxx) macro at any case
If you are still curious about what I'm doing,
check this: https://github.com/ZFFramework/ZFFramework
I'm using lots of tricks to achieve fully dynamic reflection in pure C++,
to achieve some of my fancy thoughts,
for now, this project is just for fun,
I have no idea whether it has practicability
EDIT2:
For now, I think the best workaround should be like this:
#define NAMESPACE_BEGIN(ns) \
namespace ns { \
extern const char *__curNS();
#define NAMESPACE_END(ns) \
}
#define NAMESPACE_REG(ns) \
const char *__curNS() {return #ns;}
app level users still only need to care about NAMESPACE_BEGIN
NAMESPACE_REG must be declared exactly once, in source file
if not, undefined symbol would happen
if more than once, duplicated symbol would happen
although it's annoying and sometimes you need additional source file
to hold the NAMESPACE_REG,
the strict rule should prevent user from forgetting the ugly workaround
You are making much fuss over something that is trivial to implement.
First of all, use of NAMESPACE_BEGIN and NAMESPACE_END seems unnecessary to me. I don't see how that is more readable or useful than
namespace Foo
{
}
If getting the name of the namespace is important/useful, add a trivial function.
namespace Foo
{
inline std::string get_name() { return "Foo"; }
}
Small sized real world applications need thousands of lines of code. Large sized real world applications need millions of lines of code. From that perspective, implementing a one line inline function is a very minor task.
This solution employs a bit of preprocessor magic and has these features:
Namespace is mentioned only once
Access to a macro containing the unquoted name
Access to a macro containing the quoted name
Support for repeating the same namespace
Support for different namespaces
Misuse of the BEGIN/END macros is detected
Cleanup, i.e. no extra macros defined outside the BEGIN/END block
It does not support nested namespaces.
Example of usage:
#include "framework.hpp"
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
// Here you have access to NAMESPACE_NAME (unquoted, i.e. Foo)
// and also to NAMESPACE_NAME_STRING (quoted, i.e. "Foo")
#include NAMESPACE_END
// NAMESPACE_NAME and NAMESPACE_NAME_STRING do not exist
// outside the block, so they cannot be misused
// Different namespaces in the same TU are supported
#define NAMESPACE_NAME Bar
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
// Repeating the same namespace is also supported
#define NAMESPACE_NAME Foo
#include NAMESPACE_BEGIN
inline std::string f()
{
return NAMESPACE_NAME_STRING;
}
#include NAMESPACE_END
The implementation follows:
framework.hpp
#pragma once
#define NAMESPACE_BEGIN "framework_namespace_begin.hpp"
#define NAMESPACE_END "framework_namespace_end.hpp"
framework_namespace_begin.hpp
#ifndef NAMESPACE_NAME
#error "NAMESPACE_NAME not defined"
#endif
#define NAMESPACE_IN_NAMESPACE 1
#define NAMESPACE_NAME_DO_STR(X) #X
#define NAMESPACE_NAME_STR(X) NAMESPACE_NAME_DO_STR(X)
#define NAMESPACE_NAME_STRING NAMESPACE_NAME_STR(NAMESPACE_NAME)
namespace NAMESPACE_NAME {
framework_namespace_end.hpp
#ifndef NAMESPACE_IN_NAMESPACE
#error "NAMESPACE_IN_NAMESPACE not defined"
#endif
}
#undef NAMESPACE_NAME
#undef NAMESPACE_NAME_STRING
#undef NAMESPACE_IN_NAMESPACE
You know what? I think I might just have a viable solution for this. It's actually very simple, and it's very close to the OP's original suggestion (which really only had the problem of a potential duplicate definition if you wanted to open the namespace twice in the same translation unit). You just have to think a bit laterally and not be too precious about seeing your namespaces being bracketed by macros instead of curly braces.
So let me just lay it out here, because there's really nothing to it, and then I'll explain why I personally happen to like it.
Code:
Macros:
#define DECLARE_NAMESPACE(ns) \
namespace ns {\
static constexpr const char *_curNamespace = #ns; \
}
#define BEGIN_NAMESPACE(ns) \
namespace ns { \
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
#define END_NAMESPACE }
Sample code:
#include <iostream>
DECLARE_NAMESPACE (Foo)
BEGIN_NAMESPACE (Foo)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
BEGIN_NAMESPACE (Foo)
void another_print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
DECLARE_NAMESPACE (BarBar)
BEGIN_NAMESPACE (BarBar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
END_NAMESPACE
int main ()
{
Foo::print_namespace_name ();
Foo::another_print_namespace_name ();
Bar::print_namespace_name ();
Bar::BarBar::print_namespace_name ();
}
Output:
Foo
Foo
Bar
BarBar
Now this is obviously very straightforward to implement and also easy to use and has no obvious limitations. In particular, it can handle nested namespaces (as shown in the code above) and opening a namespace twice in the same compilation unit also works (again, this is shown in the code snippet).
But, but, but, don't we still have to type in the name of the namespace twice, and wasn't that the very thing we were trying to avoid to eliminate typos?
Well, sure, we have to type the name in twice, but so what, live with it. Point is, with this particular set of macros, the compiler will now catch any typos for us. Let's prove that by deliberately putting one in. So this:
DECLARE_NAMESPACE Whoops
BEGIN_NAMESPACE whoops
END_NAMESPACE
Generates this (I couldn't find a better way of formulating the static_assert, sorry):
prog.cc:12:24: error: '_curNamespace' is not a member of 'whoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:27:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (whoops)
^~~~~~~~~~~~~~~
And more importantly this (and this is why we need the BEGIN_NAMESPACE macro):
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
DECLARE_NAMESPACE (BarWhoops)
BEGIN_NAMESPACE (Barwhoops)
END_NAMESPACE
END_NAMESPACE
Generates this:
prog.cc:12:24: error: '_curNamespace' is not a member of 'Bar::Barwhoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:42:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (Barwhoops)
^~~~~~~~~~~~~~~
Which is just dandy.
So, you know, what's not to like?
Live demo - uncomment line 3 to see those compiler errors.
you can use a variable and change its value with 'NAMESPACE_BEGIN' and 'NAMESPACE_END'
the variable __name represent the current full namespace position
like "abc::def::detail"
std::string __name = "";
std::string & __append(std::string & str, const char * ptr) {
if (!str.empty()) {
str.append("::");
}
str.append(ptr);
return str;
}
std::string & __substr(std::string & str, const char * ptr) {
if (str.length() == strlen(ptr)) {
str.clear();
} else {
str = str.substr(0, str.length() - strlen(ptr) - 2);
}
return str;
}
#define NAMESPACE_NAME __name
#define CONCATENATE_DIRECT(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
#ifdef _MSC_VER
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __COUNTER__)
#else
# define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#endif
#define APPEND_NAME(x) std::string ANONYMOUS_VARIABLE(__start_name) = __append(__name, #x)
#define SUBSTR_NAME(x) std::string ANONYMOUS_VARIABLE(__end_name) = __substr(__name, #x)
#define NAMESPACE_BEGIN(x) \
namespace x { \
APPEND_NAME(x);
#define NAMESPACE_END(x) \
SUBSTR_NAME(x); \
}
then you can use the NAMESPACE_NAME macro for the full name or you can extract the last name from it
Here's a way. The core idea came from the line of thought:
Q: How can I define multiple things with the same name accessible from the same scope?
A: Make them all functions with different parameter types. And if all of them have identical bodies, it doesn't matter which one gets called.
Q: How can I generate an unlimited set of different parameter types?
A: A class template.
Q: How can I make sure a call to that set of overloaded functions will never be ambiguous?
A: Make sure the binary relation "is implicitly convertible from" is a complete ordering on the parameter types, and use a unique minimal element for the argument type.
#include <type_traits>
#include <functional>
struct NamespaceHandleObj {
template <const NamespaceHandleObj* Handle1, const NamespaceHandleObj* Handle2>
struct less : public std::bool_constant<std::less<>{}(Handle1, Handle2)> {};
};
template <>
struct NamespaceHandleObj::less<nullptr, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle1>
struct NamespaceHandleObj::less<Handle1, nullptr> : public std::false_type {};
template <const NamespaceHandleObj* Handle2>
struct NamespaceHandleObj::less<nullptr, Handle2> : public std::true_type {};
template <const NamespaceHandleObj* Handle>
struct NamespaceParamType
{
constexpr NamespaceParamType() noexcept = default;
template <const NamespaceHandleObj* Other,
typename = std::enable_if_t<NamespaceHandleObj::less<Other, Handle>::value>>
constexpr NamespaceParamType(NamespaceParamType<Other>) noexcept {}
};
#define NAMESPACE_UTILS_TOSTR1(x) #x
#define NAMESPACE_UTILS_TOSTR(x) NAMESPACE_UTILS_TOSTR1(x)
#define BEGIN_NAMESPACE(ns) \
namespace ns { \
namespace { \
constexpr NamespaceHandleObj namespace_handle_{}; \
constexpr const char* current_ns_(
NamespaceParamType<&namespace_handle_>) noexcept \
{ return NAMESPACE_UTILS_TOSTR(ns); } \
}
#define END_NAMESPACE }
#define CURRENT_NAMESPACE (current_ns_(NamespaceParamType<nullptr>{}))
The code above is C++17, but it wouldn't be hard to port it to previous versions, even all the way to C++03.
I've seen some responses to the too few template-parameter-lists problem in classes but I'm getting a similar error in a header file where the declarations seem to be at the name space level. I'm not at all a C++ guy, but I want to figure out what I need to correct in this file to prevent the following error regarding the two lines after the typedef:
too few template-parameter-lists
Here's a file experpt
#ifndef SH_AUDATA_H
#define SH_AUDATA_H
#include "BinaryIO.h"
#include "AudioData.h"
// AuMuLawAudioData
typedef AudioDataImpl<AuMuLaw,BYTE> AuMuLawAudioData ;
inline BYTE AuMuLawAudioData::readValue (BinaryIO* io) const { return (io->read8()) ; }
inline void AuMuLawAudioData::writeValue (BinaryIO* io, BYTE v) const { io->write8(v) ; }
#endif // ndef SH_AUDATA_H
Thanks!
You either need to
indicate your defining a template member using template<> (http://ideone.com/ZWYuCd)
or you should just use the full qualified class name (http://ideone.com/zsmHjG)
Contrast both:
template <typename T> struct X
{
T v;
void foo();
};
typedef X<int> SomeTypedef;
#if 0
template<> void SomeTypedef::foo() {
//
}
#else
template <typename T> void X<T>::foo() {
//
}
#endif
int main()
{
SomeTypedef a;
a.foo();
}
Both the approaches to define foo will work (change #if 0 to #if 1 to "select" the other approach)
The following code explains the situation I encountered:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
extern "C"{
#endif
extern pod* pod_new(short f, char b);//BANG!!!
extern char pod_foobar(pod* p); //BANG!!!
#ifdef __cplusplus
}
#endif
I can't put the C linkage functions inside the namespace ns, or the C client won't find their definitions. When I pull them out of the namespace, the C++ definition won't compile either,all because of the pod solution, which I learned from C++ FAQ Lite,it's only a preprocessor trick. And sadly, this trick couldn't deal with namespaces.
What am I supposed to do? Should I throw out all type-safety, and replace pod* with void*, or is there a nicer solution for this kinda situation? Any advices? Please!
I'd just give two different declarations for the functions
for C++:
extern "C" ns::pod* pod_new(short f, char b);
extern "C" char pod_foobar(ns::pod* p);
and for C:
typedef struct pod pod;
extern pod* pod_new(short f, char b);
extern char pod_foobar(pod* p);
But if this doesn't satisfy you, for C++ you could also have a typedef
typedef ns::pod ns_pod;
for C
typedef struct ns_pod ns_pod;
and then have the same common function prototype.
extern ns_pod* pod_new(short f, char b);
extern char pod_foobar(ns_pod* p);
Edit: In C the struct pod or struct ns_pod is an incomplete type, so in C directly you could never do anything that uses the fields or asks for its size. The difference between a pointer to an incomplete type and a void* is that you can only assign such a struct pointer to another struct pointer of the same incomplete type.
typedef struct ns_pod2 pod2;
ns_pod* q = pod_new(...); // valid
ns_pod2* r = pod_new(...); // a constraint violation! (= compiler error)
The second one would need an explicit cast, if you want to insist. This is one of the reasons why casts are frowned upon by many C programmers.
I figured it out myself :) by checking the symbols of the obj files with nm.
It turns out that the C++ namespaces have no effects on functions with C linkage, therefore I can rewrite the code above like this:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
namespace ns{
extern "C"{
#endif
pod* pod_new(short f, char b);
char pod_foobar(pod* p);
void pod_free(pod* p);
#ifdef __cplusplus
}
}
#endif
I'm writing a code generator, well, actually a data generator that will produce data structures of this form (obviously the actual data structures are much more elaborate):
typedef struct Foo {
int a;
struct Foo* foo;
} Foo;
extern Foo f1;
extern Foo f2;
Foo f1 = {1, &f2};
Foo f2 = {2, &f1};
This is portable for all C and C++ compilers I have tried.
I would like to forward declare these struct instances as static so as not to pollute the global variable space, as in:
typedef struct Foo {
int a;
struct Foo* foo;
} Foo;
static Foo f1;
static Foo f2;
static Foo f1 = {1, &f2};
static Foo f2 = {2, &f1};
Although this works with gcc and probably all C compilers, the above code does not work with C++ compilers and results in a compile error:
error: redefinition of ‘Foo f1’
error: ‘Foo f1’ previously declared
I understand why this is happening in C++. Is there a simple workaround that does not involve using code at runtime to achieve the same effect that is portable to all C++ compilers without resorting to using a C compiler to compile certain files?
This should compile with either C or C++ and give you the same name to access the same thing in both compilers.
#ifdef __cplusplus
namespace // use anonymous namespace to avoid poluting namespace.
{
struct StaticFoos
{
static Foo f1;
static Foo f2;
};
Foo StaticFoos::f1 = {1, &StaticFoos::f2};
Foo StaticFoos::f2 = {2, &StaticFoos::f1};
}
static const &Foo f1 = StaticFoos::f1;
static const &Foo f2 = StaticFoos::f2;
#else
static Foo f1 = {1, &f2_};
static Foo f2 = {1, &f1_};
#endif
Now in C and C++, you can access f1 and f2.
This seems to have a similar effect to Josh's answer, but with less complexity:
#ifdef __cplusplus
namespace {
extern Foo f1;
extern Foo f2;
Foo f1 = {1, &f2};
Foo f2 = {2, &f1};
}
#else
static Foo f1;
static Foo f2;
Foo f1 = {1, &f2};
Foo f2 = {2, &f1};
#endif
When compiled for C++, the extern definitions for f1 and f2 are exposed in the object file with an externally linkable symbol; however, because they are inside an anonymous namespace the symbols are mangled in such a way that they will not conflict with symbols from another translation unit.
With macro magic you could set things up so there's only one place where f1 and f2 are declared and defined, but if this is being generated mechanically there's probably not much reason to do that.
Something like:
#ifdef __cplusplus
#define START_PRIVATES namespace {
#define END_PRIVATES }
#define PRIVATE extern
#else
#define START_PRIVATES
#define END_PRIVATES
#define PRIVATE static
#endif
START_PRIVATES
PRIVATE Foo f1;
PRIVATE Foo f2;
Foo f1 = {1, &f2};
Foo f2 = {2, &f1};
END_PRIVATES
What you are trying to avoid is termed as Static Initialization Order Fiasco. You will be well served to use functions instead and also to initialize the individual objects with default values and then reseat the member pointers.
Your code samples mean radically different things. You will have to relook. The first one succeeds because you have a definition of one object and a declaration of another. This will work with both C and C++.
extern Foo f1;
This is a declaration and a tentative definition.
static Foo f1;
This is the declaration and definition of an object f1 of type struct Foo.
static Foo f2;
Ditto.
static Foo f1 = {1, &f2};
This is a redefinition. You have violated the One Definition Rule which says, there must be one and exactly one definition of a symbol in a translation unit. Outside of that you are allowed to have multiple definitions but of course each such occurrence has to have the same syntax and semantics.
static Foo f2 = {2, &f1};
Ditto.
extern Foo fn;
/* some code */
extern Foo fn;
/* some more ... */
Foo fn; /* finally a definition */
This is fine since it's ok to have multiple tentative declarations.
You can't forward declare objects, only types. The extern solution is the correct solution. Or if you really need to avoid global namespace pollution, make them static and initialize them using a function which you call before all others.
EDIT: Michael Burr mentioned the reason in a comment, I figured I'd add it to a post:
#dirkgently: it is valid C because the
C standard says: "Within one
translation unit, each declaration of
an identifier with internal linkage
denotes the same object or function".
C++ has no such rule.
EDIT:
As noted in one other post. You can use an anonymous namespace as well to limit the scope of the variable. Just wrap the namespace stuff in an #ifdef __cplusplus and you should be good to go.
I have encountered this problem. The restriction is frustrating and I don't see any reason why C++ has this gratuitous incompatibility with C.
My solution is to use static functions - which you can forward declare - which just return f1 and f2:
typedef struct Foo {
int a;
struct Foo* foo;
} Foo;
static Foo* link_f1();
static Foo* link_f2();
static Foo f1 = {1, link_f2()};
static Foo f2 = {2, link_f1()};
static Foo* link_f1() { return &f1; }
static Foo* link_f2() { return &f2; }
Unfortunately this isn't valid C so you'll still need different code for C and C++.
I would create two files (.cpp and .h):
code.h:
typedef struct Foo {
Foo() {}
Foo(int aa, struct Foo* ff) : a(aa), foo(ff) {}
int a;
struct Foo* foo;
} Foo;
static Foo f1;
static Foo f2;
code.cpp:
void myfun()
{
f1 = Foo(1, &f2);
f2 = Foo(2, &f1);
}
I would also prefer to put all variables like f1, f2 ... into some kind of "storage" object (of my own class or e.g. some STL container). Then, I would define this object as a static one.
Here is what I did in my project. Instead of trying to solve this with an anonymous namespace, I used a named namespace.
[ And then thanks to Matt McNabb's helpful comments, it turns out that an anonymous namespace will do for a super tidy solution with fewer macros that generates no external name pollution. ]
This allows me to have two separate regions of program text, with regular file scope in between, for a tidy solution:
Everything is hidden behind these macros:
#ifdef __cplusplus
#define static_forward(decl) namespace { extern decl; }
#define static_def(def) namespace { def; }
#else
#define static_forward(decl) static decl;
#define static_def(def) static def;
#endif
And so we can do:
static_forward(struct foo foo_instance)
void some_function(void)
{
do_something_with(&foo_instance);
}
static_def(struct foo foo_instance = { 1, 2, 3 })
The C expansion is straightforward, looking like this:
static struct foo foo_instance;
void some_function(void)
{
do_something_with(&foo_instance);
}
static struct foo foo_instance = { 1, 2, 3 };
The C++ expansion looks like this:
namespace { extern struct foo foo_instance; }
void some_function(void)
{
do_something_with(&foo_instance);
}
namespace { struct foo foo_instance = { 1, 2, 3 }; }
So, in a nutshell, thanks to anonymous namespaces, C++ does not actually have a static forward reference problem, only the problem of implementing it in a C incompatible way, which is bridgeable with macros.
Multiple anonymous namespace regions in the same translation unit are the same namespace, and the surrounding file scope outside of the namespace has visibility into it.