Jump to content


C++ not quite public's


13 replies to this topic

#1 squint

    New Member

  • Members
  • PipPip
  • 14 posts

Posted 28 November 2009 - 09:39 AM

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();

};


#2 martinsm

    Member

  • Members
  • PipPip
  • 88 posts

Posted 28 November 2009 - 11:39 AM

You could simply write:
#define private public


#3 squint

    New Member

  • Members
  • PipPip
  • 14 posts

Posted 28 November 2009 - 02:50 PM

martinsm said:

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.

#4 Nautilus

    Senior Member

  • Members
  • PipPipPipPip
  • 351 posts

Posted 28 November 2009 - 05:20 PM

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.
-Nautilus

(readin' this? you ought to get out more)


#5 SyntaxError

    Valued Member

  • Members
  • PipPipPip
  • 139 posts

Posted 28 November 2009 - 05:22 PM

"friend" doesn't do what you need?

#6 squint

    New Member

  • Members
  • PipPip
  • 14 posts

Posted 28 November 2009 - 05:55 PM

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.

#7 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 28 November 2009 - 06:05 PM

squint said:

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.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#8 squint

    New Member

  • Members
  • PipPip
  • 14 posts

Posted 28 November 2009 - 06:26 PM

.oisyn said:

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.

#9 deanmat

    New Member

  • Members
  • Pip
  • 2 posts

Posted 29 November 2009 - 07:50 PM

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.

#10 squint

    New Member

  • Members
  • PipPip
  • 14 posts

Posted 29 November 2009 - 08:34 PM

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.

#11 deanmat

    New Member

  • Members
  • Pip
  • 2 posts

Posted 29 November 2009 - 08:50 PM

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.

#12 JarkkoL

    Senior Member

  • Members
  • PipPipPipPip
  • 474 posts

Posted 30 November 2009 - 10:12 AM

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.

#13 SyntaxError

    Valued Member

  • Members
  • PipPipPip
  • 139 posts

Posted 30 November 2009 - 11:59 PM

squint said:

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.

#14 Reedbeta

    DevMaster Staff

  • Administrators
  • 5305 posts
  • LocationBellevue, WA

Posted 01 December 2009 - 12:45 AM

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.
reedbeta.com - developer blog, OpenGL demos, and other projects





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users