0

Suppose I want to create the the Following class:

#pragma once
#include <memory>
#include <string>

namespace stackquestion
{
    struct Logger
    {
        void Log(std::string message);
    private:
        class Impl;
        std::unique_ptr<Impl> impl;
    };
}

When I want to publish the class I end up depending on my consumers definition of std::string and std::unique_ptr. I am vague on the meaning of publish. I am thinking of handing someone a library either for static or dynamic linking.

When I fall back to a version without those includes I end up losing the comfort / safety that I wanted to gain.

#pragma once

namespace stackquestion
{
    struct Logger
    {
        void Log(const char *);
    private:
        class Impl * impl;
    };
}

Is there a silver bullet that I am missing?

Johannes
  • 6,490
  • 10
  • 59
  • 108
  • 4
    The good thing about the standard library is that is is the ***standard*** library. All conforming C++ compilers will have it. The classes in the `std` namespace is not your *consumers*, it's their C++ compilers. – Some programmer dude Jan 18 '19 at 16:31
  • 4
    If the consumer has a custom `` or `` that breaks your code that's their problem, not yours. You guarantee you code is standard compliant and you absolve yourself of any blame. – NathanOliver Jan 18 '19 at 16:33
  • My code is compliant to the version at that time. `std::string` changed in the past making stack corruption possible when passed between translation units. All participants were compliant to something and the stack was corrupted. – Johannes Jan 18 '19 at 16:39
  • 2
    @Johannes If this is going to be a DLL then you're right you should not be passing around standard containers: https://stackoverflow.com/questions/5347355/can-i-pass-stdstring-to-a-dll – NathanOliver Jan 18 '19 at 16:43
  • 1
    @Johannes *All* object files, libraries etc that you link together should *always* (exceptions exist of course, but as a general rule) be built with the *exact same* compiler - which implies the same standard library. C++ has never guaranteed binary compatibility between compilers nor compiler versions. – Jesper Juhl Jan 18 '19 at 17:01
  • @JesperJuhl Yeah, that basically cuts to the chase. What If I can not guarantee that. I could start asserting my compiler as a macro but that may be needlessly restrictive. – Johannes Jan 21 '19 at 10:13

1 Answers1

1

Both solutions are fine, depending on what your goal is.

Including memory and string

Doing this, doesn't break code, you do force them to have C++11 or above to use your library. However, I see this as a plus point as you don't have to mess around with a lot of tricks to support C++98.

When compiling the code, they shouldn't mess with the standard library, so if they do something like #defined unique_ptr shared_ptr I would blame your users for bad coding, not you. And yes, you could try to protect from a lot of bad stuff done by the users, as overloading operator& (address of), however, you would end up with code like the STL implementations, which ain't pretty either.

Using pimpl

Using pimple resolves a lot of the issues mentioned above. However, you shouldn't use it because of that. The only real advantage of pimple is binary compatibility.

As you don't expose the STL or any other libraries except for your own, you shouldn't get link errors on std::string. (Yes, this is possible)

If you compile your library with libc++, std::string is actually std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > with a memory layout specific for libcxx.

If you compile your library with libstdc++, std::string becomes std::basic_string<char, std::char_traits<char>, std::allocator<char> > (or a longer name for the C++11 variant)

See this thread for more details

JVApen
  • 11,008
  • 5
  • 31
  • 67