Question

First off, sorry for the title. I couldn't really condense what I'm trying to ask into one phrase :(

I was reading this post, and it somehow got me thinking on function pointers. Specifically, I was wondering why it's "bad" (or, at least, rarely seen) to pass class member functions as function parameters, and then use that pointer on an existing object within that function.

Let's assume I have a template class "Container", which stores a single variable of type T and provides a method to get a const reference to this variable.

template<class T>
class Container {
public:
    Container(T anObject) {
        m_data = anObject;
    }
    const T& getData() const {
        return m_data;
    }
private:
    T m_data;
};

Now, I would like to be able to execute member functions of T on m_data, but I don't want to make getData() non-const because that would enable all kinds of other mischief with the returned reference. My solution is to add a new public function, modifyData(...), to Container, which takes a function pointer to a member function of T as a parameter and executes it on m_data; like so:

// ...
void modifyData( void(typename T::*funcptr)(void) ) {
    (m_data.*fptr)();
}
// ...

As-is, this will crash and burn if T is a pointer. For testing, I just created a specialized template for Container<T*> to address this, but I'm sure there would be a more elegant way.

A very construed example shows that this seems to work as intended:

// example class to be used with Container
class Data {
public:
    Data() {m_count = 0; }
    void incrementCount() { m_count++; }
    int getCount() const { return m_count; }
private:
    int m_count;
};

// ... in main.cpp:
Data dat;
Container<Data*> DCont(dat);
std::cout << cl.getData()->getCount() << std::endl; // outputs 0
DCont.modifyData<Data>(&Data::incrementCount);
std::cout << cl.getData()->getCount() << std::endl; // outputs 1

// compiler catches this:
// DCont.modifyData<SomeOtherClass>(&Data::incrementCount); 
// this probably does something bad:
// DCont.modifyData<SomeOtherClass>(&SomeOtherClass::someFunc); 

Now, instinctively this just seems like a horribly twisted way of doing things, and I've never seen code that works like this. But my question is, is there a performance/security reason why something like this is bad, or is it something that's just considered bad practice? If it's "just" bad practice, then why is that?

Obvious limitations that I could think of are that something like // DCont.modifyData(&SomeOtherClass::someFunc); will probably crash at runtime, but I think that could be addressed by checking the type of U against T in incrementData(). Also, as it is, modifyData only accepts void (*)() functions, but this could probably be addressed with variadic templates.

This example is obviously very construed and not implemented so well, but I think (hope?) it's good enough to explain what I'm talking about.

Thanks!

EDIT: There seems to be some confusion as to what the question is. Basically, this is the scenario I'm talking about: You have a bunch of classes from some library that you're trying to store in the container, and another function that generates certain containers; Now, you want the user to be able to call existing member functions on the objects within these containers, but not to modify the actual objects (like when returning a non-const reference with the getter). An actual implementation would probably use some sort of variadic template to be useful, but I need to think that through some more before posting example code.

In short, I'd like to limit a user's access to container members to only member functions of that member. Is there an easier way of doing this, or does this way not work in the way I was intending?

Was it helpful?

Solution

I don't have any problem with your architecture - I don't see it as bad practice. To me it seems quite a laborious way to protect data and doesn't really help you much in that the user can use any void function to modify the contained data which isn;t really a contract on what can and can't be changed.

I think the reason this construct is so rarely seen is that your requirement and goals of the container class are unusual.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top