1
votes

I've got a function

foo(int, int, int, int/long long/_int64/double/long double/char *, int ONLY IF previous char * - otherwise unneeded)

Problem is, char * implementation is different than the value types due to requiring a string copy. Yes, this is an interface to old code, so i cannot use std::string :/

Currently, I've got it as a template, and a function overload for char *, with the extra argument. However, all operations for the other types are also valid on char *, so if the caller forgets the last argument, the function silently matches the template instead, producing the wrong logic.

Is there any way I can force the usage of the overload/use default arguments in function templates/something that will allow for a different signature (extra argument) for a specific type without silently matching a lesser signature?

More: No access to C++11 yet, but I'm open to see a suggestion using it to help push for adoption. No boost, but same as above

I have also tried

return_type foo(int,int,int,typename std::enable_if<!std::is_pointer<T>::value, T>::type & value2update)

without any luck. It then claims that the call with a double & parameter fails to match.

2
What exactly do you mean with "otherwise unneeded"? This should be implementable easily through overloading.Xeo
I assume the extra int is a length? Could you take a StringPiece instead to avoid the need for special-casing entirely?Scott Lamb
@Xeo, yeah I just wanted to avoid code duplication for each type so wanted to templatize the other types, which all share exactly the same code. @ Scott Lamb, yes, for sure that is a valid - and likely the best - option. However, this integration will happen in around 3000 places in the codebase, so having to add in the StringPiece creation logic in the calling code was something I would have liked to avoid. I was just hoping there was something I was doing wrong to force specialization - have to investigate Thomas' answer more thoroughlyim so confused
If this is how you wanted to go, you could look into clang tools for automatic refactoring. I hear it's awesome for this kind of tedious change, though I've never tried it myself.Scott Lamb

2 Answers

2
votes

You could make the linker help you. Declare, but do not define, your function template and char* overload in a header:

template<typename T>
foo(int, int, int, T);
foo(int, int, int, char*, int);

In the implementation file (.cpp/.cc), implement both:

template<typename T>
foo(int, int, int, T) { ... }

foo(int, int, int, char*, int) { ... }

And explicitly instantiate versions for the types you want to accept:

template<>
foo(int, int, int, int);
template<>
foo(int, int, int, long long);
// etc.

If I'm understanding correctly, ScottLamb suggests something like this in the comments. Header:

foo(int, int, int, int);
foo(int, int, int, long long);
...
foo(int, int, int, char*, int);

Implementation file (.cpp/.cc):

namespace {
    template<typename T>
    foo_tmpl(int, int, int, T) { ... }
}

foo(int, int, int, int) { foo_tmpl(...); }
foo(int, int, int, long long) { foo_tmpl(...); }
....
foo(int, int, int, char*, int) { ... }

This is preferable from the perspective of someone using the header (they can see immediately which overloads are available) but takes slightly more work on the implementation side.

1
votes

Use a helper class string_ref that bundles the both parameters for you:

class string_ref {
  const char *str;
  std::size_t len;
public:
  string_ref(std::string const& s)
    : str(s.c_str()), len(s.size()) {}

  string_ref(const char *c, std::size_t len)
    : str(c), len(len) {}

  const char * c_str() const { return str; }
  std::size_t size() const { return len; }
};

And then

foo(int, int, int, int/long long/_int64/double/long double/const char*, [int])

Just add

foo(int, int, int, string_ref)
{
   foo(int, int, int, c_str(), size());
}

A string_ref can be created from a const char* and int with no overhead and both can be extracted easily for further passing to C functions.

(The naming string_ref is not random.)