Reputation: 23
I recently found a function declaration which confuses me:
void CopyAndBindStaleTables( DescriptorHandle DestHandleStart,
ID3D12GraphicsCommandList* CmdList,
void (STDMETHODCALLTYPE ID3D12GraphicsCommandList::*SetFunc)(UINT, D3D12_GPU_DESCRIPTOR_HANDLE));
So in this declaration the third parameter is a function pointer. But I have a hard time understand why we need to put the class name ID3D12GraphicsCommandList:: there.
Any idea? Thanks
Upvotes: 2
Views: 105
Reputation: 145269
It's a member function pointer. Which is not directly a pointer to a function, but sufficient info about it that combined with a reference or pointer to object, the function can be called. Virtually if it's virtual.
You can think of it as a kind of offset in a class' vtable (since most every extant C++ implementation uses vtables).
It's generally a good idea to avoid using member function pointers because they make it easy to inadvertently break the type system, and the rules that come into play then are so subtle that even experts have to think twice and look it up.
Here's an example of how member function pointers allow a very unsafe hack, that might look safe because there is no explicit type cast, but that still ends up crashing in a failed assertion. I added const
as usual in this code (it's a pretty construed example), but all the const
stuff can just be ignored, removed. The problem here stems from a risky use of a member pointer that allows access to a base class' protected parts via a reference to an object of base class type:
#include <string>
using namespace std;
class Collection
{
private:
int n_items_{ 0 };
protected:
auto n_items_value() const -> int { return n_items_; }
auto n_items_var() -> int& { return n_items_; }
public:
virtual auto n_items() const
-> int
{ return n_items_; }
virtual ~Collection() = 0;
};
Collection::~Collection() {}
class List
: public Collection
{
private:
mutable bool is_spliced_{ false };
void count_the_items() { n_items_var() = 42; }
public:
auto n_items() const
-> int override
{
if( is_spliced_ )
{
const_cast<List*>( this )->count_the_items();
is_spliced_ = false;
}
return n_items_value();
}
void splice() { is_spliced_ = true; }
};
namespace impl {
struct Ungood_hack
: Collection
{
// Look ma! No virtual call overhead! Yay!
static auto fast_n_items( Collection const& o )
-> int
{
return (o.*&Ungood_hack::n_items_value)();
}
};
} // namespace impl
auto fast_n_items( Collection const& o )
-> int
{ return impl::Ungood_hack::fast_n_items( o ); }
#include <assert.h>
auto main() -> int
{
List o;
o.splice();
#ifdef TEST_IT
(void) o.n_items(); // Updates the count.
#endif
assert( fast_n_items( o ) == 42 ); // !Oops.
}
Here's an example of how the same kind of trick, but now for a data member, can be used to advantage, accessing the protected
collection member of a std::stack
in order to find the current stack size:
#include <stack>
using namespace std;
template< class Type >
auto n_items( stack<Type> const& st )
-> int
{
struct Hack: stack<Type>
{
static auto n_items( stack<Type> const& st )
-> int
{ return (st.*&Hack::c).size(); }
};
return Hack::n_items( st );
}
#include <assert.h>
auto main() -> int
{
stack<int> st;
st.push( 1 ); st.push( 2 ); st.push( 3 );
assert( n_items( st ) == 3 ); // OK.
}
Upvotes: 2
Reputation: 40859
Because member function pointers are different from function pointers and member function pointers to functions in one class are different from any other class. Different types need different declarations.
Upvotes: 0