1

I'm having trouble finding why the following very small program doesn't compile:

#include <stdint.h>

// Returns the alpha value of a texel at position (x, y) from src.
static inline uint8_t get_alpha(const uint8_t *src, int rowBytes, int x, int y) {
    return *(src + y*rowBytes + x);
}

typedef uint8_t (*GetAlphaProc)(const uint8_t* src, int rowBytes, int x, int y);

template<GetAlphaProc getAlphaProc>
static void compress_block(uint8_t** dst, const uint8_t* src, int rowBytes) {
    // Do some trivial stuff
    uint64_t out = 0x0000000001FE000173ULL;
    uint8_t a = getAlphaProc(src, rowBytes, 0, 0);
    *(reinterpret_cast<uint64_t*>(*dst)) = (static_cast<uint64_t>(a) << 32) | out;
}

int main() {
    // Initialization 
    uint8_t src[144]; for (int i = 0; i < 144; ++i) src[i] = i;
    uint64_t out;
    uint64_t* dst = &out;
    uint8_t** dstPtr = reinterpret_cast<uint8_t**>(&dst);

    compress_block<get_alpha>(dstPtr, src, 12);
}

The errors that are being produced are:

[user ~/test]$ g++ --version
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[user ~/test]$ g++ test-linkage.cpp -o testl
test-linkage.cpp: In function ‘int main()’:
test-linkage.cpp:25:46: error: ‘get_alpha’ is not a valid template argument for type ‘uint8_t (*)(const uint8_t*, int, int, int) {aka unsigned char (*)(const unsigned char*, int, int, int)}’ because function ‘uint8_t get_alpha(const uint8_t*, int, int, int)’ has not external linkage
test-linkage.cpp:25:46: error: no matching function for call to ‘compress_block(uint8_t**&, uint8_t [144], int)’
test-linkage.cpp:25:46: note: candidate is:
test-linkage.cpp:11:13: note: template<uint8_t (* getAlphaProc)(const uint8_t*, int, int, int)> void compress_block(uint8_t**, const uint8_t*, int)

The question is why the error has not external linkage matters for g++? All of the functions involved in this code have internal storage, and from most of the documentation that I was able to google, this is allowed for static functions. How come function template arguments must have external linkage when being compiled in a single module?

EDIT

Similar build problems under clang++:

[user ~/test]$ clang++ --version
Ubuntu clang version 3.0-6ubuntu3 (tags/RELEASE_30/final) (based on LLVM 3.0)
Target: x86_64-pc-linux-gnu
Thread model: posix

[user ~/test]$ clang++ -std=c++11 test-linkage.cpp -o testl
test-linkage.cpp:25:5: error: no matching function for call to 'compress_block'
    compress_block<get_alpha>(dstPtr, src, 12);
    ^~~~~~~~~~~~~~~~~~~~~~~~~
test-linkage.cpp:11:13: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'getAlphaProc'
static void compress_block(uint8_t** dst, const uint8_t* src, int rowBytes) {
            ^
1 error generated.
3
  • This g++ 4.6.3 should be emphasized maybe. @mokosha Get a newer compiler. Commented Jul 28, 2014 at 18:36
  • Is my version of clang too old, too? Commented Jul 28, 2014 at 18:42
  • @Mokosha I don't know for sure, I don't have that version installed, but the one I do have (clang 3.4.2) accepts it. Commented Jul 28, 2014 at 18:54

2 Answers 2

3

It simply used to be a hard rule, no exceptions, that template arguments could not have internal linkage. It was a hard rule because there was not yet enough implementation experience to tell where it would be okay to have such template arguments.

In C++11, that changed, because we now know that compilers can handle that without much of a problem. Template arguments can now have internal linkage. To compile in C++11 mode, pass -std=c++11 to the command-line arguments, or -std=gnu++11 to keep some more extensions enabled.

Note that millsj's answer correctly points out a different problem in your code, and fixing the one you're asking about will make GCC report that too.

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

5 Comments

Seems this sample disproves what you're saying. There's the std=c++11 flag set, the compiler error is issued though. So this doesn't really answer the question IMHO (unless I've missed something essential, and you're patient enough to enlighten me).
I've updated my code to pass the correct pointer. I'm still getting the error message using g++ 4.6.3, even if I add the -std=c++0x flag (-std=c++11 and -std=gnu++11 both are unrecognized)
@πάνταῥεῖ That's the different error, the one millsj's answer mentioned... See how there's no mention of linkage in the error message?
@Mokosha Sorry, this is implemented in GCC 4.7, not in 4.6. If you need this to work in GCC 4.6, you'll have to remove static.
I tried it with g++ 4.8.3 and -std=gnu++11, and the code works.
1

compressed_block is expecting a uint8_t** as its first parameter, but you're giving it a uint64_t*

static void compress_block(uint64_t* dst, const uint8_t* src, int rowBytes)

This compiles and runs for me on VS2013.

4 Comments

This is entirely correct, but not what the question is asking about; fixing that won't get rid of the error GCC is reporting.
@πάνταῥεῖ Only in C++11 mode.
@hvd That's set there (it's my default, this is the current standard)
I've updated my code to pass the correct variable, and updated the associated error message.

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.