C++ not quite public's

A4956f14412244c2942ff6746b81fc04
0
squint 101 Nov 28, 2009 at 09:39

Once in a while I write a member function which a few classes in my library need to use but which isn’t intended to be available to external clients of the library. I’ve wished that C++ had a way of stating which functions were in the published API and which were internal, rather than just public,protected,private for everyone.

I came up with the following as a random idea. It is trivially simple and it works, but comments are welcome - can anyone see any issues with it? Can anyone see a way to make it work for protected functions too? I’m fire proof, so if you have the knee jerk reaction to flame anything you haven’t seen before please feel free.

It requires a macro defined for just the project, in much the same way that we define PROJECTNAME_DLL_EXPORT for windows builds.

In the project header file:

#ifdef MY_PROJECT_MACRO
#define public_internal public
#else
#define public_internal private
#endif

and then in class headers:

class example
{
public_internal:
    void youCantCallMe();
};

13 Replies

Please log in or register to post a reply.

8fd4a055522ce713cde7dd1cb4083cb2
0
martinsm 101 Nov 28, 2009 at 11:39

You could simply write:

#define private public
A4956f14412244c2942ff6746b81fc04
0
squint 101 Nov 28, 2009 at 14:50

@martinsm

You could simply write:

#define private public

Ah, no, because then ALL private member functions would be public internally. If I wanted to do that I could just make everyone in my library a friend.

D619d95cddb1edb227f51ef539d15cdc
0
Nautilus 103 Nov 28, 2009 at 17:20

Hello.

Yours is a different approach to implement the ‘friend’ functionality.
Friend or not, an evil cast is enough to hijack all access privileges you set.

In point of fact, in C++ so long you ‘expose’ something (though not in the strict sense of the programming term) it is accessible.
Yet it’s kind of pointless to worry about it.

To your aid is the fact that a non-public member function is always an hazard to invoke out of proper context.
It should be automatic that if *you* make it not public, there’s a reason why *I* am not supposed to call it.
I don’t need to know what the reason is.
I just see ‘private’ and I know it’s stuff I must not toy with… lest I bug my own program in new and unexpected ways.

With that said, if a private function of yours can be called freely (with nothing bad happening at all) your member function could be made public.
And if (once public) it exposes unnecessary functionality to the end-user, your function has no need to be member of your class.

Look I’m not trying to pontificate.
But there’s an OOP guideline the rich C++ syntax all too often leads people to forget:
Do it because it’s necessary, not because you can do it

Cheers.

5225bc0c3bf66f4c275c332de6388d1f
0
SyntaxError 101 Nov 28, 2009 at 17:22

“friend” doesn’t do what you need?

A4956f14412244c2942ff6746b81fc04
0
squint 101 Nov 28, 2009 at 17:55

In any given project of over a certain level of complexity there will be functionality grouped into modules which consist of multiple classes, just as a class groups smaller items like integers. Where a class exposes an interface of functions, so a module exposes an interface of classes. However, where a class can signify that functions are accessible to anyone or only within the class (public/private), a module has no such options. This little define does nothing more than allow me to restrict access to certain functions to other members of the module - a sort of larger scale OO.

“friend” does not. Nor does making everything public.

PS At no point did I try to claim this was a secure way of protecting a class from intentional misuse. That’s a ridiculous idea. I’m trying to protect the class from accidental misuse, which is very valuable.

340bf64ac6abda6e40f7e860279823cb
0
_oisyn 101 Nov 28, 2009 at 18:05

@squint

It is trivially simple and it works

No, it doesn’t. The accessibility of a function can be used in the name mangling scheme, resulting in link errors. Perhaps it works on the compiler you’re using, but that says nothing about the others. Your code won’t work on MSVC++, for example.

A4956f14412244c2942ff6746b81fc04
0
squint 101 Nov 28, 2009 at 18:26

@.oisyn

No, it doesn’t. The accessibility of a function can be used in the name mangling scheme, resulting in link errors. Perhaps it works on the compiler you’re using, but that says nothing about the others. Your code won’t work on MSVC++, for example.

Ah, thankyou - that’s the kind of answer I needed :yes:

hmmm, oh well, back to the drawing board.

85eb8a5cee7f826b813e95f48aa5f7be
0
deanmat 101 Nov 29, 2009 at 19:50

There really isn’t a way to do exactly what you want to do, with the safety you are looking for. However, there is a pattern you can use to achieve what you are looking for.

Let’s say you have a class like this:

class ClassInternal
{
public:
    void FunctionA();
    void FunctionB();
    void FunctionC();
    void FunctionD();

};

But you only want to “publish” FunctionA and FunctionB. Create a new class that contains ClassInternal and provides a pass-through interface to only those methods you want to expose.

class ClassPublished
{
public:
    void FunctionA()   { Internal.FunctionA(); }
    void FunctionB()   { Internal.FunctionB(); }

private:
    ClassInternal Internal;
};

Also, let’s say that in future you wish to have FunctionD become published, simply add it to the ClassPublished class and it is now available.

This doesn’t solve the problem of a developer bypassing the use of the published class and directly using the internal class, but it gives you a fighting chance at exposing just the published interface you want and leaving a larger surface available for internal code.

But my bigger concern, above all, is why your design needs such an approach. My first suggestion is to revisit the design before considering the above.

A4956f14412244c2942ff6746b81fc04
0
squint 101 Nov 29, 2009 at 20:34

The pimpl pattern you describe there causes private functions to be hidden so that they are invisible to the client, instead of merely private. In addition to making for a cleaner published interface it also maintains binary compatibility when the size of the private part of the class changes. These are good things.

It doesn’t, however, allow other parts of the library/module to access functions that external code can’t, so not really an answer :/

No, I think I will have to write it off as impossible, and either use friends or police the lower level interfaces more thouroughly.

Thanks though.

85eb8a5cee7f826b813e95f48aa5f7be
0
deanmat 101 Nov 29, 2009 at 20:50

Okay, now I’m understanding your problem in better detail. Your question isn’t a C++ question, it is a question about how to expose functions in a compiled library, I had missed that when I originally read your post. So, if that is the case, then yes, there isn’t a way to do that.

However, I’m quite curious as to the structure of what you are doing. I’d like to know more detail because I think there is a good solution, but I’m vague on the details of what you are doing.

What is it that is exported in the library…
Is it an actual C++ class that is exported?
Is it some functions and then you provide a C++ class that wraps that library?

I’m unclear what is exported by the library and how that code is accessed.

Fe8a5d0ee91f9db7f5b82b8fd4a4e1e6
0
JarkkoL 102 Nov 30, 2009 at 10:12

Just add a nested class which provides the functionality in its public interface to protected/private parts of the class and hide the class from your library distribution.

5225bc0c3bf66f4c275c332de6388d1f
0
SyntaxError 101 Nov 30, 2009 at 23:59

@squint

Ah, thankyou - that’s the kind of answer I needed :yes: hmmm, oh well, back to the drawing board.

Ok now I understand what you are trying. Before I figured I must be missing something because I couldn’t believe anyone would actually be doing that or it would actually work (no offense :)). If it’s all that critical I would still just use friend between classes and just make everything private that isn’t supposed to be in used external to the module. It’s true you lose the class level protection but then as the module writer you can take care of that manually without too much trouble. IMHO (and at the risk of getting flamed) protection mechanisms get way to much attention. They are a useful tool to be sure but it’s not like they’re 100% necessary. I mean your program is still going to run if everything is public.

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 168 Dec 01, 2009 at 00:45

A pattern I’ve used on occasion to solve this sort of problem is to expose an interface and factory function in a public header, and leave the actual class definition internal to the module (either in an internal header or in a source file).

For instance, you can have this in a public header:

// interface class - contains only what you want to expose to the outside world
class IFoo
{
public:
    virtual ~IFoo () = 0;    // virtual dtor is necessary, else your internal class's dtor won't be called!
    virtual void PublicMethod1 () = 0;
    virtual int PublicMethod2 (float bar) = 0;

    // you can also have public member variables here, if you like
};

IFoo * CreateFoo ();

Then in an internal header or source file:

class CFoo: public IFoo
{
public:
    // implement your public stuff
    void PublicMethod1 ();
    int PublicMethod2 (float bar);

    // you can also have module scope stuff, not exposed in IFoo
    void ModuleMethod ();

private:
    // stuff only this class can access
    float m_bar;
    void InternalMethod();
}

IFoo * CreateFoo()
{
    return new CFoo;
}

This obviously has some drawbacks, e.g. that all public methods must be virtual and you end up with two names in the namespace (IFoo, CreateFoo) instead of one. But it’s still handy for some applications. This is kind of a lightweight version of COM, without all the reference counting and QueryInterface stuff.