1

Due to templates being templates, they need to be defined in a header file (making explicit instantiations is not an option). I have an extern function which takes an expanded template parameter pack:

#pragma once

extern "C" int bar(...);

class Foo {
    template <typename ...Args>
    int foo(Args&&... args) {
        return bar(args...);
    }
};

I thought about it for quite a while, and I can't figure out how to decouple this. I don't want bar to be in the header and the consumers of said header to have access to it, but because templates must be in a header file, I can't make a wrapper function that would contain the extern. The extern function cannot take va_list.

Is this even possible to do in a portable (compiler agnostic) way?

PS: the extern could be really anywhere, as long as it would somehow bypass templates requirement to be withing the header, and not be accessible to consumers of the header.

13
  • 1
    No, that's why modules were added in C++20 Commented Jul 28 at 18:57
  • Remember that declarations of globally-visible objects/functions are not limited to global scope. Commented Jul 28 at 19:01
  • does the function need to be extern "C" ? if not then you can declare it inside foo Commented Jul 28 at 19:01
  • 1
    templates dont have to be in the header. the definition just needs to be visible in the translation unit that use it Commented Jul 28 at 19:19
  • 1
    @Ahmed AEK — “No, that's why modules were added in C++20”. More exactly, this is because the ridiculous wanna-be-modularity of C++ based on includes was a major annoyance for many years, an extremely archaic and unreliable construct, well behind of many high-level universal languages. Commented Jul 28 at 20:11

4 Answers 4

5

If you want to deny access to bar, you can do so via gcc's poison pragma. Just put the pragma after the definition of foo. Then foo can use bar but others cannot:

#pragma once

extern "C" int bar(...);

class Foo {
    template <typename ...Args>
    int foo(Args&&... args) {
        return bar(args...);
    }
};

#pragma GCC poison bar

Edit: compiler-agnostic version:

#define bar(...) static_assert (false, "bar is poisoned");

Any good?

Sign up to request clarification or add additional context in comments.

6 Comments

OP asked for a compiler-agnostic way, this pragma is definitely not agnostic
Sure, but maybe this is good enough
I kind of need it to be compiler agnostic, but it's a step in the right direction, although I would have to get something similar working on other compilers. Edit: The vc++ solution is kind of ugly, but it is something to consider
Added a compiler-agnostic way to my answer
This is probably the only solution, but I'm glad a solution for this even exists.
1

Language specifications can only appear in namespace scope, so you cannot restrict the declaration to the function :-/

One workaround which should work (maybe except for Oracle Studio See note #1) would be to use function pointer:

class Foo
{
    static int (* const fbar) (...);
public:

    template <typename ...Args>
    int foo(Args&&... args) {
        return fbar(args...);
    }
};

and in cpp:

extern "C" int bar(...);

int (* const Foo::fbar)(...) = &bar;

Note #1:

According to cppreference

The only modern compiler that differentiates function types with "C" and "C++" language linkages is Oracle Studio [..]

So I'm unsure it would work for that compiler directly (maybe a extra cast would be required).

1 Comment

I think I prefer this method since it it makes the extern function totally inaccessible.
0

The best I can think of with extern "C" is to:

  • Declare an ABI-compatible version of the function in an internal namespace that takes a private/friend type as the first parameter (such as an enum or pointer-containing struct)

  • Define your class as you wish in the same namespace, calling your overload above

  • Declare a second deleted function of the same name with const T &... parameters to catch any attempts to call the original function

I'm not sure if it then becomes outright impossible to call the original function, but I think it should at least become very difficult (similar to accessing private members), which ought to be good enough.

Comments

-1

I have an extern function which takes an expanded template parameter pack

Well, you have an extern "C" function with a C variadic argument list.

You can forward this call out-of-line the nice way if bar(...) has a matching vbar(va_list), but you still end up with a wrapper function which has to be visible at instantiation time:

class Foo {
    static int wrapper(int, ...);
public
    template <typename ...Args>
    int foo(Args&&... args) {
        return wrapper(args...);
    }
};

x.cpp

#include <cstdarg>

extern "C" int vbar(va_list);

int Foo::wrapper(int dummy, ...)
{
  va_list args;
  va_start(args, dummy);
  int rv = vbar(args);
  va_end(args);
  return rv;
}

If some loser wrote bar(...) without a matching va_list implementation, this obviously won't work, and you should complain loudly that they don't know how to use C variadics properly.

2 Comments

If I get it right, the OP can't add an extra int vbar(va_list) to int bar(...).
Unfortunatly yes I'm unable to accept add an extra vbar

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.