Typedef function vs function pointer - c++

I'm trying to understand function declaration using typedefs.
What does this code do in C++?
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
fcn_t f;
fcn_t *pf;
ptr_t pf2;
In my understanding:
fcn_t is the type of a function, and so the line with f is a function declaration (not a definition), and I could later define it like void f(void) { blabla(); bleble(); } just as if I had declared void f(void); instead of fcn_t f;;
fcn_t * is the type of a function pointer, and the line with pf is a pointer variable definition, and pf is default-initialized (assuming the code excerpt is from the global scope);
There is no difference between fcn_t* and ptr_t, thus everything I said about pf applies to pf2.
Did I get it right? Would any of the three declarations have its meaning changed if I marked them extern? What would change if the code was compiled as C instead of as C++?

Yes you are right on all three counts. The only thing that would change if you marked them extern are the function pointers. Function declarations are by default extern in C++.
Try and compile the following program
template <typename...>
struct WhichType;
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
// constexpr auto one = WhichType<fcn_t>{};
// constexpr auto two = WhichType<fcn_t*>{};
// constexpr auto three = WhichType<ptr_t>{};
fcn_t f;
void f() {}
int main() {
f();
}
Uncommenting the commented lines will likely give you a compiler error that tells you what types the WhichType instance is being instantiated with, and as a result it should show you the exact types of all three things you asked about. It's a trick I picked up from Scott Meyers' book "Effective Modern C++".
To test whether the declarations are extern or not, write two simple implementation files, with one containing the definition of the variable
main.cpp
template <typename...>
struct WhichType;
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
fcn_t f;
int main() {
f();
}
definition.cpp
void f() {}
and then compile, link and run definition.cpp and main.cpp (via g++ -std=c++14 definition.cpp main.cpp and then ./a.out). If the declaration was not extern then the compile would fail with an undefined symbol error.

Here is a simple illustration of these typedef
#include <stdio.h>
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
fcn_t f;
fcn_t *pf;
ptr_t pf2;
void
f(void)
{
printf("I am void\n");
}
void
vSayHi(void)
{
printf( "Hi\n" );
}
int
main(void)
{
pf2 = vSayHi;
pf2();
pf = vSayHi;
pf();
f();
return 0;
}
OUTPUT:
Hi
Hi
I am void

Related

Redefining a handle ptr of void* to handle ptr to struct* (C/C++ mixcode) for access

I have C/C++ mix code and want to pass around a struct that contains a reference to a class. Because of this, I can't declare this struct in the header file of the C++ component (because class is defined in source file of C++ component) but only in the source file. The main script in C however has to reference that struct somehow, so I typedef it to void*. However because of that, I can't dereference the handle type back to a struct. Redefining the handle pointer in the source file is not possible. How can I work around this?
header_with_obj.hpp
class A {
int a;
};
header.hpp
typedef void* config_handle_t;
source.cpp
#include "header.hpp"
#include "header_with_obj.hpp"
typedef struct {
A* ptr;
int some_other;
} config_t;
// typedef config_t* config_handle_t <-- error: conflicting declaration 'typedef struct config_t* config_handle_t '
int foo(void* arg)
{
config_handle_t handle = (config_handle_t) arg;
handle->A.a = 4; // <-- error: 'config_handle_t' {aka 'void*'} is not a pointer-to-object type
}
main.c
#include "header.hpp"
int main()
{
// we get that void* from somewhere and pass it in
foo(arg);
}
The usual way to do this is to use an undefined struct. In its most basic form:
void foo(struct the_config_struct *arg);
// OK even though 'struct the_config_struct' wasn't defined!
// surprisingly this is also allowed in C++
You can also make a typedef:
typedef struct the_config_struct *config_handle_t;
void foo(config_handle_t arg);
and if you want, you can even call the typedef the same thing as the struct. Just to avoid confusing people, I wouldn't do this unless it's a typedef for the struct (not a pointer).
typedef struct the_config_struct the_config_struct;
void foo(the_config_struct *arg);
You don't to actually have defined the struct until you want to access its members:
// if we uncomment this definition then it's OK
// struct my_struct {
// char *message;
// };
void foo(struct my_struct *arg) {
puts(arg->message); // error: struct my_struct is undefined
}
Finally (since this confused you before) you should know that typedef names and struct names are completely separate in C.
struct foo {}; // defines "struct foo" but "foo" is completely unrelated
typedef int bar; // defines "bar" but "struct bar" is completely unrelated
foo *get_foo(); // error: "foo" is unknown
struct foo *get_foo(); // OK
typedef struct bar foo;
foo *get_bar(); // OK: returns pointer to struct bar (not struct foo!)
struct foo *get_foo(); // this one returns pointer to struct foo
struct baz {};
typedef struct baz baz;
// now "baz" is an alternative name for "struct baz" - they are interchangeable
typedef struct baz {} baz; // short version
and structs don't have to have names:
// foo is a variable, and it's a struct variable, but the struct has no name.
// so we have no way to use the struct for anything else.
struct {
int i;
} foo;
// The struct is still there even though it doesn't have a name!
// In C++ you can write decltype(bar) to say "the same type as variable bar".
// Even though we don't know the person's name we can still yell out "Hey you in the red shirt!"
decltype(foo) foo2; // a variable foo2. The type is decltype(foo) i.e. the struct from before
// GCC lets you do it in C using "typeof".
// This is not standard. It's a special feature in GCC.
typeof(foo) foo2;
// This struct also has no name either. But the typedef means we have
// an "unofficial" way to name it, just like decltype(foo) before.
// This is valid in C as well as C++.
typedef struct {
char message[50];
} bar;

Incomplete type error when compiled with g++

I am trying to execute following code using g++ and getting incomplete type error
#include <stdio.h>
struct try_main{
union{
struct try_inner_one{
int fl;
float g;
}one;
struct try_inner_two{
char a;
}two;
}un;
int chk;
};
void func(struct try_inner_one o){
printf("%d\n",o.fl);
}
int main(){
struct try_main z = {{1,2},3};
func(z.un.one);
return 0;
}
Error:
union.c: In function ‘void func(try_inner_one)’:
union.c:15:6: error: ‘o’ has incomplete type
void func(struct try_inner_one o){
^
union.c:15:18: error: forward declaration of ‘struct try_inner_one’
void func(struct try_inner_one o){
^
union.c: In function ‘int main()’:
union.c:20:16: error: parameter 1 of ‘void func(try_inner_one)’ has incomplete type ‘try_inner_one’
func(z.un.one);
Above code is successfully getting compiled with gcc
What is the reason for this error and how to fix this
Thanks
C and C++ have different scoping rules. The full name of the type in C++ isn’t struct try_inner_one, since the type definition is nested inside the unnamed union inside try_main.1
If you want to write code that works equally in both C and C++, pull the type definition to the top level:
struct try_inner_one {
int fl;
float g;
};
struct try_inner_two {
char a;
};
struct try_main {
union {
struct try_inner_one one;
struct try_inner_two two;
} un;
int chk;
};
1 The fully qualified name of this type can’t be spelled in C++ since the type it’s nested inside is unnamed. You could give a name to the union type, that would allow you to spell the fully qualified name of try_inner_one in C++. However, that name wouldn’t be legal C code, since C doesn’t have a scope resolution operator.
If you want to keep the nested type definition you could give the union a name (in the following, union_name) and do the following to keep the code compiling for both C and C++:
// (Type definition omitted.)
#ifdef __cplusplus
using try_inner_one = try_main::union_name::try_inner_one;
#else
typedef struct try_inner_one try_inner_one;
#endif
void func(try_inner_one o){
printf("%d\n", o.fl);
}
What is the reason for this error
The reason is that try_inner_one nested within the union nested within try_main cannot be found by unqualified name lookup in the context outside of that union in C++ (unlike in C).
how to fix this
You can use a qualified name in C++:
void func(decltype(try_main::un)::try_inner_one o){
You can simplify if you give a name for the union:
union u { // note the name
struct try_inner_one{
void func(try_main::u::try_inner_one o){
A cross-language compatible solution is to define the struct outside of each other as described in Kondrad Rudolph's answer.
A word of warning: C++ is more restrictive than C on how inactive members of union can be accessed.
It seems you are compiling your program as a C++ program. In this case each declaration within the structure try_main has the scope of this structure.
So you need to declare the function like
void func( decltype( try_main::un )::try_inner_one o );
or
void func( const decltype( try_main::un )::try_inner_one &o );

"C linkage function cannot return C++ class" in Visual Studio 2019

I have the following function:
class Foo;
template <typename T>
struct PyArray1D{
std::size_t size;
T *array;
};
extern "C" PyArray1D<Foo> SimulatePhotonEvents()
{
Foo * foo = new Foo();
return {1, foo}
}
However this does not compile using VS2019 (however it does with gcc) with the warning
C linkage function cannot return C++ class
However it does work when the template argument is a double for example... I can return a 'PyArray1D', without VS complaining.
So I added a new struct:
struct FooArray {
std::size_t size;
Foo *data;
};
And return this from the extern C function.
extern "C" FooArray SimulatePhotonEvents()
{
Foo * foo = new Foo();
return {1, foo}
}
And to my big surprise this worked! My question is, why?
Isn't VS smart enough to see that the FooArray get's created out of the template function? And are there other, less hacky ways to solve this?
You can make this compile by adding an explicit template specialization for PyArray1D<Foo> to your code:
template struct PyArray1D<Foo>;
I don't actually know why. I guess the compiler is not able to see the template, if it is not explicitly specialized, while parsing the extern declaration

Opaque pointer in MSVC generates compiler error

main.c
#include "stackg.h"
int main()
{
return 0;
}
stackg.h
#ifndef STACKG_H
#define STACKG_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stack_gt* stack_gt;
stack_gt stkg_init(
void* (*alloc)(const void* data, const int size),
void (*dealloc)(void* data),
void (*copy)(void* data_d, const void* data_s),
const int size
);
void stkg_free(stack_gt s);
int stkg_is_empty(stack_gt s);
int stkg_is_full(stack_gt s);
const int stkg_size(const stack_gt s);
void stkg_clear(stack_gt s);
int stkg_push(stack_gt s, const void* data);
int stkg_pop(stack_gt s, void* data);
int stkg_peek(stack_gt s, void* data);
#ifdef __cplusplus
}
#endif
#endif
The above program compiles successfully with the GCC compiler, but in MSVC2008 it gives the following error :
error C2040: 'stack_gt *' differs in levels of indirection from 'stack_gt'
What should I tell MSVC to make it compile the program without changing anything in the code?
Edit
Error occurs at line 8 of stackg.h :: typedef struct stack_gt* stack_gt;
Edit 2
If nothing else, I'll go with typedef struct _stack_gt* stack_gt;
The problem is that here:
typedef struct stack_gt* stack_gt;
you are giving stack_gt a different type, while this works fine:
typedef struct stack_gt* stack_gtB;
clang gives us a nicer error message:
error: typedef redefinition with different types ('struct stack_gt *' vs 'stack_gt')
This is covered in the draft C++ standard section 7.1.3 The typedef specifier paragraph 6:
In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. [ Example:
class complex { / ... / };
typedef int complex; // error: redefinition
—end example ]
Using the same name though is fine, so this would be ok:
typedef struct stack_gt stack_gt;
covered in paragraph 3:
In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers. [ Example:
typedef struct s { / ... / } s;
typedef int I;
typedef int I;
typedef I I;
—end example ]
Another idea:
#ifdef __cplusplus
extern "C" {
typedef void * stack_gt
#else
typedef struct stack_gt* stack_gt;
#endif
This is ugly, but you don't need to rewrite any other part of the code, only this header included in C++ . It is only used as an opaque pointer in C++ anyways, and C doesn't notice.

Method of a template class uses a typedef from the global scope. Compiler bug?

I have a code in VC2010 which I've reduced to a small example.
Test.h:
#pragma once
template <typename TPixel>
struct Image
{
typedef TPixel PixelType;
};
template <typename TImageType>
struct Operation
{
void DoOperation()
{
ImageType::PixelType value = 0;
// I've done a misprint here. It should be TImageType::PixelType
}
};
Test.cpp:
void Test()
{
typedef Image<char> ImageType;
Operation<ImageType> op;
op.DoOperation();
}
As I expected, that produces an error.
test.h(14): error C2653: 'ImageType' : is not a class or namespace name
Now, let's change test.cpp just a little bit.
typedef Image<char> ImageType;
void Test()
{
Operation<ImageType> op;
op.DoOperation();
}
Now it compiles! Surprisingly enough, ImageType in DoOperation() now matches with the global typedef in test.cpp.
My question: why does it compile? Is that a Visual C++ bug or a standard behavior?
I take it that test.cpp includes test.h before the typedef, so it's actually
#include "test.h"
typedef Image<char> ImageType;
void Test()
{
Operation<ImageType> op;
op.DoOperation();
}
Completed like this, it is indeed a bug, or standard nonconforming behavior with regard to two phase lookup. Names that do not depend on a template parameter should be resolved relative to the point of declaration of the template.
I guess this is known behavior.