16

I was searching SVM libraries and encountered BudgetedSVM.

In the source code, I found an unusual usage, just like this:

#sample.h

#ifndef SAMPLE_H
#define SAMPLE_H

//no header included or namespace declared here

#ifdef __cplusplus
extern "C" {
#endif

//no header included or namespace declared too

class Sample: public Parent
{
public:
    Sample();
    ~Sample();

    type0 fun(type1 val1, type2 val2);
    ...
};

#ifdef __cplusplus
}
#endif

#endif // SAMPLE_H

As seen, no extra header or namespace is needed in the header, which all are in the cpp file.

Here are my thoughts:

  1. Why does extern "C", which usually is used for C interfaces, group the C++ class? Is the usage in this way good for something?

  2. Even if type0, type1 and type2 appeared, their own headers are not included here, but in the cpp file (e.g. sample.h). When I call the class Sample, however, I have to include these headers (e.g. type0.h, type1.h, type2.h), which seems inconvenient.

Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
foo
  • 398
  • 1
  • 16
  • 3
    @HansPassant That's not correct, static members are immune to C linkage as well as non-static. – Rost Feb 14 '17 at 10:44
  • 4
    That's not about C, don't spam tags! And wrapping it into `#ifdef __cplusplus` is nonsense; this is apparently no valid C, thus will never be compiled with a C compiler. – too honest for this site Feb 14 '17 at 12:10
  • @Olaf : It is valid to wrap a function which takes a reference with `extern "C"` - it can even be useful if you are linking between multiple compilers with different name mangling (assuming that they both represent a reference as a pointer). – Martin Bonner supports Monica Feb 14 '17 at 16:44
  • @MartinBonner: It is useless for this code! As you wrote, you typcally wrap C function **declarations** in this construct, but wrapping `class`es does not make any sense at all. Less, the macro-wrappers (which I refer to); you just cannot validly compile this with anything else than a C++ compiler. And references are not pointers or vice-versa. It is not a matter of the language, but the ABI. But there is no C ABI for classes. – too honest for this site Feb 14 '17 at 16:48
  • You seem to be under the impression that `extern "C"` is only valid for code which will also be compiled by C compiler. I gave a counter example. I know that references are not pointers, I said "assuming [both compilers] **represent** a reference as a pointer" (emphasis added). Perhaps "implement" would have been slightly clearer than "represent". – Martin Bonner supports Monica Feb 14 '17 at 16:51
  • 1
    @MartinBonner: I did not. But it is useless to specify C linkage for code which is no valid C code, thus cannot have a C ABI. `extern "C"` does not care **how** the referenced code is generated (C, Assembly, Fortran, Brainfuck), but which ABI it uses. If there is no C ABI for the constructs, it is meaningless. – too honest for this site Feb 14 '17 at 17:46
  • The only useless part is the `#ifdef __cplusplus`, because the code will never compile `#ifndef __cplusplus` because it's C++ code. – wizzwizz4 Feb 14 '17 at 18:35
  • Related: http://stackoverflow.com/questions/1025345/extern-c-can-not-be-used-at-class-level – Jason C Feb 14 '17 at 18:55

4 Answers4

19

According to C++ Standard C language linkage is ignored in determining the language linkage of the names of class members and the function type of class member functions.

So such grouping looks meaningless, unless type0, type1 or type2 are function pointers defined inline, in this case they will have C linkage.

In case type0, type1 or type2 are user defined types and their definitions are placed in separate headers it violates the good C/C++ style rule - all headers shall be self-contained and require no additional inclusions to compile.

To answer the main question ('Why?') - it looks like C language linkage was just blindly copy-pasted from another header (possibly pure C, where it makes sense). Or it maybe done deliberately - by "uniformity" reasons.

Rost
  • 8,779
  • 28
  • 50
2

Even if type0, type1 and type2 appeared, their own headers are not included here, but in the cpp file(e.g. sample.h). When I call the class Sample, however, I have to include these header(e.g. type0.h, type1.h, type2.h), which seems inconvenience.

Do you mean you have to include them before you call func? If that is the case it's a staple of good code organization, actually. If you never use func, you'd never need to include the headers for those types. The full definition of the types is a hard requirement only for a function call involving those types, not a function declaration.

A header should not include anything beyond the minimum that makes its own inclusion compile. Other things should be forward declared only, as long as it's possible. The standard library has the iosfwd header for this purpose exactly. So that classes that declare stream operators (<< and >>) won't need to pull the entire iostream header.

The call site, that also happens to know what concrete stream it want to write to or read from, does the inclusion.

Remember the design philosophy of C++: You don't pay for what you don't need.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Thanks. As you say, if I do not use `func`, I needn't to include these types. But what about this? if, however, class `Sample` has another function, say `void func1(type4 val)`, I may have to include all the types' headers(if all are not pointers) even only if `void func1(type4 val)` is called. – foo Feb 14 '17 at 12:04
  • @zavexu - Even if they aren't pointers you don't need to include their headers unless you **call** the function. If you want to call only `func1`, you'd need only the include the extra header for `type 4`. See here for a complete example http://ideone.com/WRdMYZ – StoryTeller - Unslander Monica Feb 14 '17 at 12:09
  • That's not the case, you are missing the point. The source code doesn't include forward declarations of types. Neither the TS code, nor the library he is referencing above. – Rost Feb 14 '17 at 12:18
  • @StoryTeller - Thanks you, I see. if types are declared in the header, I just need to include the types' header the called function need, without what others need. This makes fewer compile. – foo Feb 14 '17 at 12:28
  • @Rost - I'm not missing any point, I'm working under the assumption the OP created their MCVE without copying everything pertinent. And if that's not the case then my points are still valid, since you don't always have to include **everything** like the OP believed. – StoryTeller - Unslander Monica Feb 14 '17 at 12:30
  • @zavexu - I took a peek at the library you linked. They don't forward declare all classes they use there, and that is indeed very bad design. On the whole, a good header is indeed one that compiles successfully in isolation, but it doesn't have to include a bunch of other dependencies by default. – StoryTeller - Unslander Monica Feb 14 '17 at 12:34
0

From the C view, there are multiple reasons to do this (your question 2). One is data hiding, another is improving compile times by reducing include hierarchy.

And C allows using type pointers (passing them to functions, etc.) without knowing about them / including them. Unless you're dereferencing, it's fine. So you leave dealing with struct details to the implementor functions, abstracting the details.

Another explanation from a book, check section 4.8: http://www.duckware.com/bugfreec/chapter4.html#cppcomparison

AlbusMPiroglu
  • 645
  • 3
  • 13
  • 4
    1. This code is pure C++ (uses classes) so it doesn't make sense to reference C view. 2. Even C requires to forward declare user defined types to use pointers. 3. That doesn't look to be the case - `type0`, `type1`, `type2` look like non-pointer/non-reference types. – Rost Feb 14 '17 at 10:38
  • I just downloaded the mentioned project and the example code doesn't exist as is. So it's an approximation for the question's sake. type0, type1 etc are most likely pointer types. And for example the file blas.h from the project is passable for a C compiler. – AlbusMPiroglu Feb 14 '17 at 10:47
  • Just because `blas.h` is pure C header, it doesn't define any classes, but free functions only. – Rost Feb 14 '17 at 10:56
  • 1
    Thanks for your answer! @MattAPiroglu. Since I need some extra functionality, I have tried by adding some function and deriving. The types(e.g, `type0`) can be any type, not only pointer. You can find such a header in **BudgetedSVM/src/mm_algs.h** – foo Feb 14 '17 at 11:00
0

I believe this is used when trying to produce a library which can be linked to by a standard C linker.

Regular C symbol names will be encoded, with the name and type , so a function like this:

int blah(double num);

may have a symbol name like "iblahd" (or something like that, I forget exactly) you see, the return type and the parameter types are in the symbol.

One thing that extern C does is it makes it look for the symbols in a routine C fashion, which would just be "blah"

I think for example, this is being used to create a valid C++ header, that the compiler tries uses it, will produce linking with the symbols in another C library.

Without the extern C, it would be looking for a shared library object with a function name/ encoded symbol in the C++ style.

So, to make it clearer, this is either used

  • to create an C++ API to wrap a C library.

or

  • to create a C++ library, which can be linked to by a C application

It is possible that it would also cause the class structure to be allocated more like a struct and not to be able to use dynamic polymorphism, so that its memory layout may be correct for C (unfortunately it's been too long for me to remember for sure).

Chunko
  • 352
  • 1
  • 8