
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright 2005-2010 Google, Inc.
// Author: jpr@google.com (Jake Ratkiewicz)

// Convenience templates for defining arg packs for the FstClass operations.

// See operation-templates.h for a discussion about why these are needed; the
// short story is that all FstClass operations must be implemented by a version
// that takes one argument, most likely a struct bundling all the
// logical arguments together. These template structs provide convenient ways
// to specify these bundles (e.g. by means of appropriate typedefs).

// The ArgPack template is sufficient for bundling together all the args for
// a particular function. The function is assumed to be void-returning. If
// you want a space for a return value, use the WithReturnValue template
// as follows:

// WithReturnValue<bool, ArgPack<...> >

#ifndef FST_SCRIPT_ARG_PACKS_H_
#define FST_SCRIPT_ARG_PACKS_H_

namespace fst {
namespace script {
namespace args {

// Sentinel value that means "no arg here."
class none_type { };

// Base arg pack template class. Specializations follow that allow
// fewer numbers of arguments (down to 2). If the maximum number of arguments
// increases, you will need to change three things:
//   1) Add more template parameters to this template
//   2) Add more specializations to allow fewer numbers of parameters than
//      the new max.
//   3) Add extra none_types to all existing specializations to fill
//      the new slots.


// 9 args (max)
template<class T1,
         class T2 = none_type,
         class T3 = none_type,
         class T4 = none_type,
         class T5 = none_type,
         class T6 = none_type,
         class T7 = none_type,
         class T8 = none_type,
         class T9 = none_type>
struct Package {
  T1 arg1;
  T2 arg2;
  T3 arg3;
  T4 arg4;
  T5 arg5;
  T6 arg6;
  T7 arg7;
  T8 arg8;
  T9 arg9;

  Package(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6,
          T7 arg7, T8 arg8, T9 arg9) :
      arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5),
      arg6(arg6), arg7(arg7), arg8(arg8), arg9(arg9) { }
};

// 8 args
template<class T1,
         class T2,
         class T3,
         class T4,
         class T5,
         class T6,
         class T7,
         class T8>
struct Package<T1, T2, T3, T4, T5, T6, T7, T8, none_type> {
  T1 arg1;
  T2 arg2;
  T3 arg3;
  T4 arg4;
  T5 arg5;
  T6 arg6;
  T7 arg7;
  T8 arg8;

  Package(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6,
          T7 arg7, T8 arg8) :
      arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5),
      arg6(arg6), arg7(arg7), arg8(arg8) { }
};

// 7 args
template<class T1,
         class T2,
         class T3,
         class T4,
         class T5,
         class T6,
         class T7>
struct Package<T1, T2, T3, T4, T5, T6, T7,
               none_type, none_type> {
  T1 arg1;
  T2 arg2;
  T3 arg3;
  T4 arg4;
  T5 arg5;
  T6 arg6;
  T7 arg7;

  Package(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6,
          T7 arg7) :
      arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5),
      arg6(arg6), arg7(arg7) { }
};

// 6 args
template<class T1,
         class T2,
         class T3,
         class T4,
         class T5,
         class T6>
struct Package<T1, T2, T3, T4, T5, T6, none_type,
               none_type, none_type> {
  T1 arg1;
  T2 arg2;
  T3 arg3;
  T4 arg4;
  T5 arg5;
  T6 arg6;

  Package(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) :
      arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5),
      arg6(arg6) { }
};

// 5 args
template<class T1,
         class T2,
         class T3,
         class T4,
         class T5>
struct Package<T1, T2, T3, T4, T5, none_type, none_type,
               none_type, none_type> {
  T1 arg1;
  T2 arg2;
  T3 arg3;
  T4 arg4;
  T5 arg5;

  Package(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) :
      arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
};

// 4 args
template<class T1,
         class T2,
         class T3,
         class T4>
struct Package<T1, T2, T3, T4, none_type, none_type,
               none_type, none_type, none_type> {
  T1 arg1;
  T2 arg2;
  T3 arg3;
  T4 arg4;

  Package(T1 arg1, T2 arg2, T3 arg3, T4 arg4) :
      arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
};

// 3 args
template<class T1,
         class T2,
         class T3>
struct Package<T1, T2, T3, none_type, none_type,
               none_type, none_type, none_type,
               none_type> {
  T1 arg1;
  T2 arg2;
  T3 arg3;

  Package(T1 arg1, T2 arg2, T3 arg3) :
      arg1(arg1), arg2(arg2), arg3(arg3) { }
};

// 2 args (minimum)
template<class T1,
         class T2>
struct Package<T1, T2, none_type, none_type,
               none_type, none_type, none_type,
               none_type, none_type> {
  T1 arg1;
  T2 arg2;

  Package(T1 arg1, T2 arg2) :
      arg1(arg1), arg2(arg2) { }
};

// Tack this on to an existing arg pack to add a return value.
// The syntax for accessing the args is then slightly more stilted,
// as you must do an extra member access (since the args are stored
// as a member of this class).
// The alternative is to declare another slew of templates for functions
// that return a value, analogous to the above.

template<class Retval, class ArgPackage>
struct WithReturnValue {
  Retval retval;
  const ArgPackage &args;

  explicit WithReturnValue(const ArgPackage &args) : args(args) { }
};

// We don't want to store a reference to a reference, if ArgPackage is
// already some reference type.
template<class Retval, class ArgPackage>
struct WithReturnValue<Retval, ArgPackage&> {
  Retval retval;
  const ArgPackage &args;

  explicit WithReturnValue(const ArgPackage &args) : args(args) { }
};

}  // namespace args
}  // namespace script
}  // namespace fst

#endif  // FST_SCRIPT_ARG_PACKS_H_
