I have got this hpp file:
struct rte_spinlock_t;
class A {
public:
void init();
private:
rte_spinlock_t* spinlock;
};
and the corresponding cpp file:
#include "A.hpp"
typedef struct {
int lock;
} rte_spinlock_t;
void A::init()
{
}
Now, compiling like this: g++ A.cpp I get this error:
A.cpp:5:3: error: conflicting declaration ‘typedef struct rte_spinlock_t rte_spinlock_t’
5 | } rte_spinlock_t;
| ^~~~~~~~~~~~~~
In file included from A.cpp:1:
A.hpp:2:8: note: previous declaration as ‘struct rte_spinlock_t’
2 | struct rte_spinlock_t;
| ^~~~~~~~~~~~~~
Obviously making the struct named it works, unfortunately I cannot control the typedef of struct rte_spinlock_t that is in a library.
How could I workaround this?
I expect to be able to forward an unnamed struct without getting into conflicting declaration.
From what I understand what you have is more-less this (erroneous code):
https://godbolt.org/z/zarsTq6oE
Why don't you use pimpl (private implementation) idiom for extra level of indirection and hiding the "gory details"?
Let's assume my X is your A, lib.h contains the troublesome typedef:
//example.cpp
#include "example.hpp"
#include "lib.h"
struct Impl
{
CStruct* cs;
};
void X::init()
{
clib_init(&impl->cs);
}
X::X()
{
impl = std::make_unique<Impl>();
}
X::~X() = default;
//example.hpp
#pragma once
#include <memory>
struct Impl;
struct X
{
X();
~X();
//rest of special functions omitted for brevity
//feel free to add them at your own leisure
void init();
std::unique_ptr<Impl> impl;
};
Live demo: https://godbolt.org/z/4jYGrYEjr
BTW, I made an assumption that your troublesome struct is C code based on some searching...
Related
Most Pimpl examples look as follows:
UPDATE: both cases fail, i.e. with and without namespaces. See answer from R Sahu at https://stackoverflow.com/a/57103016/2712726 . class Impl must be qualified with class name Simpl
// Simple.h
#include <memory>
class Simple {
struct Impl; // WORKS!
std::unique_ptr<Impl> impl_;
public:
Simple();
~Simple();
int get() const;
};
But this seems to fail in the real world where you would use namespaces.
When namespaces are present, then the forward declaration has to be moved before the class declaration. Can anyone explain why?
// Simple.h but now with name spaces
namespace weired::example {
struct Impl; // OK! forwad declaration must go here
class Simple {
//struct Impl; // ERROR! incomplete type in ctor definition
std::unique_ptr<Impl> impl_;
public:
Simple();
~Simple();
int get() const;
};
}
I have tested this with gcc9 and clang8 with -std=c++11 up to c++2a.
Just for completeness, here are the Simple.cpp and main.cpp files so you can run the example yourself:
// Simple.cpp
#include "Simple.h"
namespace weired::example {
struct Impl {int a,b;};
Simple::Simple() : impl_(new Impl{3,4}) {}
Simple::~Simple() = default;
int Simple::get() const
{
return impl_->a;
}
}
and
// main.cpp
#include "Simple.h"
#include <iostream>
int main () {
auto nonsense = weired::example::Simple{};
std::cout << nonsense.get() << '\n';
}
You may forward declare Impl inside the class and outside the class, regardless of whether Simple is defined in a namespace or globally.
The implementation of Impl is almost identical in both cases.
Without namespace
Option 1 (Impl is a peer class)
The .h file
struct Impl;
class Simple { ... };
The .cpp file
// Define Impl
struct Impl { ... };
// Define Simple
Option 2 (Impl is a nested class)
The .h file
class Simple
{
struct Impl;
...
};
The .cpp file
// Define Impl
struct Simple::Impl { ... }; // Need the Simple:: scope here.
// Define Simple
With namespace
Option 1 (Impl is a peer class)
The .h file
namespace weired::example {
struct Impl;
class Simple { ... };
}
The .cpp file
namespace weired::example {
// Define Impl
struct Impl { ... };
// Define Simple
}
Option 2 (Impl is a nested class)
The .h file
namespace weired::example {
class Simple
{
struct Impl;
...
};
}
The .cpp file
namespace weired::example {
// Define Impl
struct Simple::Impl { ... }; // Need the Simple:: scope here.
// Define Simple
}
I have 1 .h file test.h containing a class. In this class there's a private method returning a pointer on a type I don't want to make "public", but I want to be able to include this test.h file in other source files.
In general, it's easy to use a forward declaration in the .h file:
class Foo;
But the problem is that this type comes from a C file that I cannot change (because it's someone else code that I'm not maintaining) and it's a typedef.
So basically my test.cpp is:
// this type comes from a C file, cannot be changed to struct or class
typedef struct
{
int x;
} Foo;
#include "test.h"
static Foo foo;
Foo *Test::private_function()
{
foo.x = 12;
return &foo;
}
int Test::compute()
{
auto *local_foo = private_function();
return local_foo->x;
}
and my test.h file is:
#pragma once
struct Foo;
class Test
{
public:
Test() {}
int compute();
private:
Foo *private_function();
};
trying to compile that fails:
>g++ -std=c++11 -c test.cpp
In file included from test.cpp:10:0:
test.h:3:8: error: using typedef-name 'Foo' after 'struct'
test.cpp:7:3: note: 'Foo' has a previous declaration here
Currently my workaround is to return void * and perform static_cast back and forth, but I don't find that optimal. Is there a nicer solution?
(I have checked Forward declaration of a typedef in C++ but I tested the solutions and they don't seem to work in my case, maybe what I want to do is simpler/different - I have just a .h and .cpp - or just not possible)
Return this:
//#include "SecretFoo.h"
struct SecretFoo {
uintptr_t handle;
};
//#include "SecretFooImpl.h"
#include "SecretFoo.h"
#include "Foo.h" // definition of typedef struct {int x;} Foo;
Foo* Crack( SecretFoo foo ) {
return reinterpret_cast<Foo*>(foo.handle);
}
SecretFoo Encase( Foo* foo ) {
return {reinterpret_cast<uintptr_t>(foo)};
}
now we get:
#include "SecretFooImpl.h"
static Foo foo;
SecretFoo Test::private_function()
{
foo.x = 12;
return Encase(&foo);
}
int Test::compute()
{
auto *local_foo = Crack(private_function());
return local_foo->x;
}
and in your header:
#pragma once
#include "SecretFoo.h"
class Test
{
public:
Test() {}
int compute();
private:
SecretFoo private_function();
};
this boils down to the same binary code, but the SecretFoo and paired Crack/Encase functions provide a safer kind of casting than just a void*.
This technique is sometimes used in the C world. SecretFoo is a kind of handle; an opaque pointer-like structure. The data in it (the uintptr_t handle) is in this case just a cast pointer; but it could be a pointer into a table of pointers or whatever else. The Crack and Encase methods are the only ways permitted to access/create the SecretFoo.
Unfortunately typedefs cannot be forward-declared.
A common workaround is to have a C++ class that inherits from the C struct, referenced by its typedef, and you can forward-declare that. This will require some code changes, but they should be minimal.
(Posting on behalf of https://stackoverflow.com/users/3943312/sam-varshavchik who commented)
This is simplified code just to show my question:
main.cpp
#include "one.hpp"
#include <iostream>
int main(){
One one;
std::cout << one.two->val;
}
one.hpp:
struct Two; <- forward declare Two
struct One{
One();
~One() { delete two;}
Two* two;
};
one.cpp
#include "one.hpp"
struct Two{
int val;
};
One::One(): two(new Two()) {}
When compiling this I get error invalid use of incomplete type 'struct Two'.
I assume that since Two is incomplete type I just cannot refer to its fields...
I am wondering is there any way to hide Two implementation in one cpp file and use it in another cpp file using this kind of forward declaration? Question comes from creating API where I would like to hide implementation on some classes.
You cannot delete an object of incomplete type.
The solution is to define the destructor in one.cpp, too.
one.hpp:
struct One {
~One();
// ...
};
one.cpp:
// ...
One::~One() { delete two; }
Wikipedia: "Opaque pointers are a way to hide the implementation details of an interface from ordinary clients, so that the implementation may be changed without the need to recompile the modules using it. ":
Header file released to clients:
struct opaque;
struct interface
{
~interface();
void test();
opaque* _p;
};
Header file not released to clients:
struct opaque
{
void test();
//...
};
interface implementation file:
#include "interface.h"
#include "opaque.h"
interface::~interface()
{
delete _p;
}
void interface::test()
{
_p->test();
}
// ...
opaque implementation file:
#include "opaque.h"
void opaque::test()
{
// actual implementation
}
following is my header file
#ifndef _ASYNCHRONOUSCLASS_H
#define _ASYNCHRONOUSCLASS_H
#include "stdafx.h"
#include <windows.h>
typedef int (*functionCall)(void *);
typedef void * voidPtr;
class AsynchronousFunction{
//int returnVal;
//functionCall fCall;
//voidPtr param;
//HANDLE m_hEvent;
struct pImpl;
pImpl* m_pImpl;
public:
AsynchronousFunction(functionCall fCall, voidPtr param);
~AsynchronousFunction();
void functionExecuter();
int result();
protected:
private:
};
#endif
In the cpp file I want to implement the struct which contains following details.
*//int returnVal;*
*//functionCall fCall;*
*//voidPtr param;*
*//HANDLE m_hEvent;*
How can I implement this ? What would be suitable, forward declaration or pointer implementation ?
In a single translation unit you will need to provide the definition of the type. It will look like:
struct AsynchronousFunction::Impl {
// members and functions...
};
Note that I renamed pImpl into Impl, the p in the idiom is for pointer, the member in the containing class would be Impl* pImpl;.
I have a problem with class member function templates. It seems that gcc is trying to analyze the code semantically too early, without explicit template instantiation.
Let the code speak
A.h (the base class):
#ifndef __A_H__
#define __A_H__
class A {
public:
A* ptr;
template<class T>
void method() {
((B*) ptr)->invoke();
}
};
#endif
B.h (the ancestor):
#ifndef __B_H__
#define __B_H__
#include<cstdio>
class B : public A {
public:
void invoke() {
printf("Method invoked\n");
}
};
#endif
main.cpp:
#include"A.h"
#include"B.h"
int main() {
A a;
a.ptr = new B();
a.method<int>();
}
This code compiles and runs fine in Visual Studio 2010 and fails to compile with following error on gcc 4.5:
In file included from main.cpp:1:0: A.h: In member function ‘void
A::method()’: A.h:10:5: error: ‘B’ was not declared in this scope
A.h:10:7: error: expected primary-expression before ‘)’ token
A.h:10:9: error: expected ‘)’ before ‘ptr’
Why is gcc trying to compile code of A::method<>() before any instantiation is requested? Which compiler behaves according to the standard?
UPDATE: Now I know that Visual Studio behaves incorrectly. B is not template parameter dependant, so it's not looked up at instantiation, but during parsing of A.h. (relevant gcc doc)
The code incorrectly compiles in Visual Studio due to the way templates are processed (basically they are ignored until they get instantiated). In your code, the identifier B is not dependent on the template arguments (T) and as such, it must be available in the context where the template is defined.
As B inherits from A, that makes everything even more complicated, as you have a cyclic dependency in the code. I would reconsider the design, and if you consider that it needs to be so, I would merge the two header files into a single one:
// merged.h
struct A {
A* ptr;
template<class T>
void method();
};
struct B : public A {
void invoke() {
printf("Method invoked\n");
}
};
template <typename T>
void A::method() {
((B*) ptr)->invoke();
}
Still, my advice would be to reconsider whether that relantionship makes sense at all, as method imposes the restriction that ptr must be a B, and that rings some bells of code smell.
ne555 from cplusplus.com forum gave me some neat solution:
new A.h:
#ifndef __A_H__
#define __A_H__
class A {
public:
A* ptr;
template<class T>
void method();
};
#include"B.h"
template<class T>
void A::method(){
((B*)ptr)->invoke();
}
#endif