0

An important functionality of MyClass is to quickly call a method of one of its members; I try to encouraging inlining of this call. The member is of type Foo. I wish to hide Foo by placing its implementation and interface in an anonymous namespace.

Here's how the relevant source and header files are currently set up...

In test.h, I have:

struct Foo;

class MyClass {
public:
    void doStuff();
private:
    Foo f;
    inline unsigned long long int code() { return f.getCode(); }
};

In test.cpp, I have:

namespace
{

struct Foo {
public:
    unsigned long long int getCode() {
        // ...
    }
};

} // end anon namespace

void MyClass::doStuff() {
    // do stuff, such as calling code()
}

The problem, of course, is that in the context of the inlined definition of MyClass::code(), f is not of a complete type.

Is there any way to hide Foo while also inlining MyClass::code()?

synaptik
  • 8,971
  • 16
  • 71
  • 98
  • Have you tried enabling link-time optimization and then checking the assembler? – goji Oct 01 '13 at 05:07
  • No, I haven't. Are you saying that perhaps it will be inlined already due to its size, and thus there may be no reason for me to flag the method as inline? – synaptik Oct 01 '13 at 05:10
  • Also, how are you calling getCode() on an incomplete type in the header? – goji Oct 01 '13 at 05:11
  • That's my point --- it's impossible. So, I'm wondering if there's another way to *hide* `Foo`... – synaptik Oct 01 '13 at 05:12
  • What do you mean by "hide"? As in implementation not being in the header? You could make f a reference as in `Foo & f` – Sam Cristall Oct 01 '13 at 05:13
  • Maybe I should just use pimpl and see what the assembly looks like. – synaptik Oct 01 '13 at 05:14
  • @SamCristall I mean to prevent `Foo` from being used elsewhere in the code (by other people). `MyClass` is really an interface to `Foo`. – synaptik Oct 01 '13 at 05:15
  • 1
    Well, with your code() being private, its only likely to be called inside the same translation unit, so should be inlined even if Foo is actually std::unique_ptr to all it to be incomplete in the header. You need to move the code() definition into the .cpp tho. – goji Oct 01 '13 at 05:15
  • OK, thanks. I guess one of my "problems" here is that I don't have a good understanding of what kinds of functions a compiler will inline. – synaptik Oct 01 '13 at 05:18
  • 1
    @synaptik Just so it's clear, `inline` rarely actually means to `inline` anymore, and all class/struct members are implicitly declared inline anyways if they are declared in the class body. It actually serves as an optional compiler hint, but more importantly its a way around the one-definition-rule. Compilers inline very well, especially if it's a simple call. As long as it's not a virtual function or a function pointer you can trust the compiler to make a good call. – Sam Cristall Oct 01 '13 at 05:18
  • @SamCristall when you say "anymore", do you mean something like most compiler releases within the last 5 years? Unfortunately, one of the machines I have to compile on is RHEL5 with gcc 4.1.2, which was released early 2007. Just curious. – synaptik Oct 01 '13 at 05:24
  • @synaptik It would be a good idea to profile it. I'm not too well versed in the history of gcc, I only know that the modern flavors of vc++, g++, clang and icc all treat it as a hint. You may be able to use the -Winline flag to help profile as per this answer: http://stackoverflow.com/a/5223872/1843316 – Sam Cristall Oct 01 '13 at 05:28

1 Answers1

1

If you store Foo inside MyClass as a pointer and then move the definition of code() into test.cpp and then only call code() from inside test.cpp, it should be inlined. There seems to be no reason why you would be calling a private member like code() in another translation unit anyway:

test.h

class MyClass
{
public:
    void doStuff();
private:
    struct Foo; // nested class, you cant access 
                // it from anon namespace in test.cpp otherwise
    std::unique_ptr<Foo> f;
    inline unsigned long long int code();
};

test.cpp

struct MyClass::Foo
{
public:
    unsigned long long int getCode()
    {
        // ...
    }
};

unsigned long long int MyClass::code()
{
    return f->getCode();
}

void MyClass::doStuff()
{
   code(); // should be inlined
}

At compile time the compiler will inline any function that it sees as beneficial based on Heuristic analysis AND if it can see the definition of the function. Which shouldn't be a problem in this case.

Link-time optimization can often take this a step further and inline functions for which the definition wasn't visible during compile time.

goji
  • 6,911
  • 3
  • 42
  • 59
  • Very informative answer. I've never done any link-time optimization (LTO). Is it true that, in general, LTO *may* make your program run faster but *will* not make it slower? I mean, if I can tolerate the added time for compilation, is it a good idea to always use LTO? (Assume optimizing for space is not important.) – synaptik Oct 01 '13 at 06:11
  • 1
    There's no guarantee either way. You simply need to try both with and without LTO and profile. Also, you would only use LTO on your release build as it adds enormously to build times. There was some articles on LTO on phoronix in the last year (for gcc/clang tho). – goji Oct 01 '13 at 06:13
  • OK, sounds good. I will try it. I'm excited --- never knew about LTO, so this opens up new possibility for me! :) – synaptik Oct 01 '13 at 06:27