5

I have an application where I'm building a function, marshal_and_apply, which calls some other function (or functor), f with some arguments. marshal_and_apply's job is to apply some special marshaling for the arguments depending on the type of f's parameters.

If one of f's parameters is of a special type, marshal_me<T>, then marshal_and_apply will marshal the parameter through some specially allocated storage before passing it to f. In order to perform the allocation, the storage requirements of all the parameters must be known to marshal_and_apply before any can be marshaled.


Some examples:

template<typename Function, typename... Args>
void marshal_and_apply(Function f, Args... args);

void func1(int x, int y);
void func2(marshal_me<int> x, int y);
void func3(marshal_me<int> x, marshal_me<int> y, marshal_me<int> z);

// this call would be equivalent to:
// func1(7,13)
marshal_and_apply(func1, 7, 13);

// this call would be equivalent to:
// auto storage = my_allocator(sizeof(int));
// auto x = marshal_me<int>(7, storage);
// func2(x, 13);
marshal_and_apply(func2, 7, 13);

// this call would be equivalent to:
// auto storage = my_allocator(sizeof(int) + sizeof(int) + sizeof(int));
// auto x = marshal_me<int>(7, storage);
// auto y = marshal_me<int>(13, storage + sizeof(int));
// auto z = marshal_me<int>(42, storage + sizeof(int) + sizeof(int));
// func3(x,y,z);
marshal_and_apply(func3, 7, 13, 42);

To solve this problem, it seems that marshal_and_apply requires a mechanism to inspect the types of f's parameters. I suspect this isn't possible in general, but it may be possible to recognize whether one of a special set of types (in this case, marshal_me<T>) is convertible to the type of a particular parameter.

How should I build marshal_and_apply?

Jared Hoberock
  • 11,118
  • 3
  • 40
  • 76
  • In your second example, I can't see what you've done with the `7`. Did you mean `auto x = marshal_me(storage, 7);` or something? – Aaron McDaid Jan 11 '12 at 21:59
  • @AaronMcDaid Yes, I've corrected it. – Jared Hoberock Jan 11 '12 at 22:04
  • If `marshal_me` has a constructor taking `int` you can call `func2(7, 13)` anyway. What additional benefit is the wrapper? – Ben Jackson Jan 11 '12 at 22:12
  • @BenJackson The example is contrived. The full explanation is beyond the scope of this post, but I need to allocate memory for function parameters in a particular way due to the unusual requirements of GPU programming. – Jared Hoberock Jan 11 '12 at 22:16

1 Answers1

4

Maybe something like this:

template<typename Function, typename... Args>
void marshal_and_apply(Function f, Args &&... args)
{
    f(InspectAndModify<Args>::process(sizeof...(Args), std::forward<Args>(args))...);
}

Now define:

template <typename T> struct InspectAndModify
{
    static T&& process(unsigned int N, T && t)
    {
        return std::forward<T>(t);
    }
};

template <typename T> struct InspectAndModify<marshal_me<T>>
{
     static T&& process(unsigned int N, marshal_me<T> && m)
     {
         /* ... */
     }
};

Something completely different: This approach first dissects the function signature, and then performs a "static transform" on each pair of types, which is where you can insert the marshal_me specialization:

template <typename T> struct marshal_me { marshal_me(T) { } };

template <typename To, typename From> struct static_transform;

template <typename T> struct static_transform<T, T>
{
  static T go(T t) { return t; }
};

template <typename T> struct static_transform<T, T&>
{
  static T go(T & t) { return t; }
};

template <typename T> struct static_transform<marshal_me<T>, T>
{
  static marshal_me<T> go(T && t) { return std::forward<T>(t); }
};

template<typename T, typename... Args>
struct marshal_impl
{
  template <typename ...Urgs>
  static T go(T(*f)(Urgs...), Args &&... args)
  {
    return f(static_transform<Urgs, Args>::go(std::forward<Args>(args))...);
  }
};

template<typename Function, typename... Args>
void marshal_and_apply(Function f, Args &&... args)
{
  marshal_impl<void, Args...>::go(static_cast<typename std::decay<Function>::type>(f),
                                  std::forward<Args>(args)...);
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Thanks! That will sort of work, but I'm afraid it wouldn't solve the third example. ```InspectAndModify::process``` needs to know the total number of parameters it needs to process before it can process any of them. – Jared Hoberock Jan 11 '12 at 22:07
  • @JaredHoberock: can you pass `sizeof...(Args)` as an additional parameter? – Kerrek SB Jan 11 '12 at 22:17
  • To which function? The signature of ```marshal_and_apply``` is fixed, but it could pass extra info to ```InspectAndModify::process```. – Jared Hoberock Jan 11 '12 at 22:24
  • Updated - I'm passing the total size to `process` now. – Kerrek SB Jan 11 '12 at 22:31
  • Got it; I should have been more specific. The problem is that ```marshal_and_apply``` does not receive ```marshal_me``` objects as parameters -- it just receives ```int```s, for example. So in your code, ```process``` would never receive a ```marshal_me```, and that specialization would never be instantiated. – Jared Hoberock Jan 11 '12 at 22:34
  • @JaredHoberock: ohh, sorry, I misunderstood! Basically you want to discriminate based on the *signature* of `Function`? – Kerrek SB Jan 11 '12 at 22:37
  • Exactly, modulo the fact that functors could have overloaded ```operator()``` with different numbers of parameters. – Jared Hoberock Jan 11 '12 at 22:39
  • @JaredHoberock: OK, I'm trying something new. It's still pretty broken, but let's see how far we get. Centrepiece of the magic is the `static_transform` template. – Kerrek SB Jan 11 '12 at 23:02
  • @JaredHoberock: Alright, I think it's fairly workable now -- give it a try! – Kerrek SB Jan 11 '12 at 23:15
  • +1 for `static_cast::type>(f)` alone – sehe Jan 11 '12 at 23:27
  • The ```T(*f)(Urgs...)``` bit looks like exactly what I would need to inspect the function signature. I've come up with a different approach for inspecting function objects. Thanks! – Jared Hoberock Jan 12 '12 at 00:40
  • @JaredHoberock: Yeah, my version only works for function pointers. You'd really want something that matches all "callable entities"... – Kerrek SB Jan 12 '12 at 00:41