1

So I've been racking my brain trying to figure out a way to something. I thought I'd post it here to see if anyone has any ideas. Consider the following:

template <typename S, typename T, T S::* pMember>
bool SortByMember(const S& L, const S& R)
{
    return L.*pMember < R.*pMember;
}

...

struct SomeStruct
{
    int SomeMember;
};

void SomeFunction(void)
{
    GetSortByMember<&SomeStruct::SomeMember>();
}

I would like the function, GetSortByMember, to return a function pointer to the corresponding instantiation of SortByMember. However, I can't think of a way to declare/define GetSortByMember in a way that doesn't require the user to also pass the class type and the member type. This:

GetSortByMember<SomeStruct, int, &SomeStruct::SomeMember>();

is overly verbose and requires me to state the member type. I'm sure there's probably a solution in the boost libraries, but I'd rather not introduce that dependency to the project I'm working on.

I doubt highly that there's a solution that'll yield the exact syntax I used in the psudocode, but perhaps something can be done with template classes or macros?

The signature of SortByMember is expected by the class that will be using the function pointer, so it can't be changed.

2 Answers2

0

Your example is not clear, presumably, you need to call the resulting function with two arguments? If so, why not use a getter function and pass that in, e.g:

#include <iostream>

struct foo
{
  int bar;
  int getBar() const { return bar; }
};

template <typename S, typename U>
bool SortByMember(const S& L, const S& R, U f)
{
    return (L.*f)()< (R.*f)();
}

int main(void)
{
  foo a = {1};
  foo b = {2};

  std::cout << SortByMember(a, b, &foo::getBar) << std::endl;
}
Nim
  • 33,299
  • 2
  • 62
  • 101
  • Two reasons. One, I may be working with classes from other libraries, ie, I cannot add a 'getBar' function to them. Two, this would be a run-time solution, whereas I'm looking for a compile-time solution. Also, I actually need a function pointer because I will be storing that pointer and then calling it later. – The Orange Man Nov 08 '11 at 23:18
  • Unrelated, this transforms the pointer to member that originally referred to a data type to be a pointer to member function. – David Rodríguez - dribeas Nov 09 '11 at 00:08
  • @TheOrangeMan: Can you further explain what you mean by a *compile* and *runtime* solution and why you *need* the former? – David Rodríguez - dribeas Nov 09 '11 at 00:08
  • Here is a good explanation of the difference between compile-time and run-time: http://stackoverflow.com/questions/846103/runtime-vs-compile-time. The reason a compile-time solution is needed is because the type of function I'm pointing to [bool (*)(const S&, const S&)] doesn't take a member pointer as a parameter. In other words, the function itself must know which member it's comparing. – The Orange Man Nov 09 '11 at 00:21
  • @TheOrangeMan: Don't take me wrong, I do know what runtime and compile time is. The reason for the question is that your requirement is not related to compile/runtime, but rather to the signature of the function: `struct comparer { static int type::*ptr; static bool compare( type const &, type const& ); };` has your signature and is resolved at runtime. On the other hand, if the template in this answer is inlined (probably it would), the function pointer is resolved at compile time, and yet it would not have your wanted signature. Why do you *need* that specific signature? Why not a functor? – David Rodríguez - dribeas Nov 09 '11 at 08:40
  • @TheOrangeMan: When you answer someone, you can use the \@user syntax to have the system notify them of the comment – David Rodríguez - dribeas Nov 09 '11 at 08:46
  • @DavidRodriguez-dribeas: Consider, `struct Foo {float Bar; float Baz};` The comparer would not allow me to have two different comparison functions for `Bar` and `Baz`. I can already get the function I need via `&SortByMember`. The question isn't how to get the function, but how to do it succinctly, without having to specify the class name an extra time, nor the member type, both of which can be inferred from `&Foo::Bar` alone. I need the signature I do because it is expected by the class that will be calling the function. – The Orange Man Nov 09 '11 at 17:15
  • @TheOrangeMan: You have not answered the question of why the exact signature is so important to you. If you only want this to be used in STL or you can perform type erasure (as with `function<>` functors), then the solution is to handle this at runtime by creating a functor that stores the member pointer and applies it in `operator()` (read the answer I suggested as duplicate). If that is not an option, you can still do it at runtime: create a global (or static member) that holds the member pointer and then use a function that uses the global pointer --not recommended, it can be done though – David Rodríguez - dribeas Nov 09 '11 at 17:40
  • Your solution of forcing the decision to be done through static typing forces you to pass all the arguments, if you don't want to pass the arguments, you can easily change it, you just need to choose the compromise that you want to take, and note that there will be no difference (most probably) in performance if you had considered performance to be a factor towards your approach. – David Rodríguez - dribeas Nov 09 '11 at 17:43
  • @DavidRodriguez-dribeas: The decision regarding the signature is not mine to make. I'll be passing the function pointer to another class that I cannot change. The class can also take comparison functions that do things other than comparing two members; that's just the most common case. Creating a functor for every single member I need to compare would be _more_ verbose than `&SortByMember`, which is the exact opposite of what I'm gunning for here. If I had to compromise, I'd use Bowie Owens' solution at the unfortunate cost of portability. – The Orange Man Nov 09 '11 at 18:37
  • @TheOrangeMan: You don't need to create a functor for *each* member that you want to compare, but rather a single functor to compare *any* member generically. I guess you did not read the [linked](http://stackoverflow.com/questions/2202731/is-there-support-in-c-stl-for-sorting-objects-by-attribute) question's answers... It contains a generic functor that can be used to compare based on a member variable, or to use another given functor to compare that particular member, and the functor itself is created without mentioning any type: `std::sort( v.begin(), v.end(), member_lt( &test::x ) );` – David Rodríguez - dribeas Nov 10 '11 at 00:30
  • @DavidRodriguez-dribeas: I read the answers. However, the functor does not provide me with the functionality I need. The class I'm working with, whose interface I _cannot change_, needs a pointer to a non-member function with the signature `bool Func(const T&, const T&)`, where T is the struct being compared. – The Orange Man Nov 10 '11 at 17:41
0

There might be a nicer way to do what you want but this works using macros and the GCC specific typeof(). I'm not sure but there might be a portable way to do typeof in the new C++ standard.

#include <iostream>

template <class P, P p>
class sort_by_member_t;

template <class S, class T, T S::*p>
class sort_by_member_t<T S::*, p> {
public:
    typedef bool (*fn_t)(S const&, S const&);

    static bool fn(S const& L, S const& R)
    {
        return L.*p < R.*p;
    }
};

#define SORT_BY_MEMBER(p) sort_by_member_t<typeof(p), p>::fn;

struct SomeStruct
{
    int SomeMember;
};


int main()
{
    bool (*fp)(SomeStruct const&, SomeStruct const&);
    fp = SORT_BY_MEMBER(&SomeStruct::SomeMember);
    SomeStruct const a = { 1 };
    SomeStruct const b = { 2 };
    std::cerr
        << (void*) fp << ' '
        << (*fp)(a, b) << ' '
        << (*fp)(b, a) << ' '
        << '\n';

    return 0;
}
Bowie Owens
  • 2,798
  • 23
  • 20
  • This is probably along the lines of the solution I'll use if I can't figure out something better. I'd prefer my code to be portable, but I am using GCC, so this would work. I'm not really in a position to use the new C++ standard, but it does provide the decltype keyword, which is essentially a standardized version of typeof. – The Orange Man Nov 09 '11 at 00:29
  • I will be interested to see a better approach as I had to do something similar to this a while back now. I think the inescapable problem is you can only pass a value to a template when you know its type. You can get its type from earlier type parameter but then you must pass two parameters. You can get the type of the expression using typeof/decltype but the only way to state the expression only once is to use a macro. I personally don't ascribe macros to be universally evil just that they should be a weapon of last resort. – Bowie Owens Nov 09 '11 at 00:53