0

Say I am making a class client. I would like client to be able to be constructed with the following types:

client(const boost::network::uri::uri &, const boost::network::uri::uri &)
client(const std::string &, const std::string &)
client(const char *, const char *)

But... I would also like all the permutations...

client(const boost::network::uri::uri &, const boost::network::uri::uri &)
client(const std::string &, const std::string &)
client(const char * &, const char * &)
client(const boost::network::uri::uri &, const std::string &)
client(const std::string &, const boost::network::uri::uri &)
client(const boost::network::uri::uri &, const char * &)
client(const char * &, const boost::network::uri::uri &)
client(const std::string &, const char * &)
client(const char * &, const std::string &)

It may be assumed that my client class, stripped down for simplicity, looks like the following.

#include <string>
#include <boost/network.hpp>

#define HOST_URI "..."
#define AUTH_URI HOST_URI"..."

namespace bn = boost::network;

class client
{

private:

  const bn::uri::uri host_;

  const bn::uri::uri auth_;

public:

  client(const bn::uri::uri & host = const bn::uri::uri(HOST_URI),
         const bn::uri::uri & auth = const bn::uri::uri(AUTH_URI));

  client(const std::string & host = const std::string(HOST_URI),
         const std::string & auth = const std::string(AUTH_URI));

  client(const char * & host = HOST_URI,
         const char * & auth = AUTH_URI);

  client(const bn::uri::uri & host = const bn::uri::uri(HOST_URI),
         const std::string & auth = const std::string(AUTH_URI));

  client(const std::string & host = const std::string(HOST_URI),
         const bn::uri::uri & auth = const bn::uri::uri(AUTH_URI));

  client(const bn::uri::uri & host = const bn::uri::uri(HOST_URI),
         const char * & auth = AUTH_URI);

  client(const char * & host = HOST_URI,
         const bn::uri::uri & auth = const bn::uri::uri(AUTH_URI));

  client(const std::string && host = const std::string(HOST_URI),
         const char * & auth = AUTH_URI);

  client(const char * & host = HOST_URI,
         const std::string && auth = const std::string(AUTH_URI));

};

and currently defined as:

#include <string>
#include <boost/network.hpp>

namespace bn = boost::network;

client::client(const bn::uri::uri & host,
               const bn::uri::uri & auth)
: host_(host), auth_(auth)
{
    ...
};

client::client(const std::string & host,
               const std::string & auth)
: client(bn::uri::uri(host), bn::uri::uri(auth)){}

client::client(const char * & host,
               const char * & auth)
: client(bn::uri::uri(host), bn::uri::uri(auth)){}

client::client(const bn::uri::uri & host,
               const std::string & auth)
: client(host, bn::uri::uri(auth)){}

client::client(const std::string & host,
               const bn::uri::uri & auth)
: client(bn::uri::uri(host), auth){}

client::client(const bn::uri::uri & host,
               const char * & auth)
: client(host, bn::uri::uri(auth)){}

client::client(const char * & host,
               const bn::uri::uri & auth)
: client(bn::uri::uri(host), auth){}

client::client(const std::string & host,
               const char * & auth)
: client(bn::uri::uri(host), bn::uri::uri(auth)){}

client::client(const char * & host,
               const std::string & auth)
: client(bn::uri::uri(host), bn::uri::uri(auth)){}

So my question is, what is the proper and easy way of doing this? Granted, I did all the permutations this time by hand, but in the future I could have 3+ variables to permute, and this would get ugly, quick.

Francisco Aguilera
  • 3,099
  • 6
  • 31
  • 57
  • If `uri` has a constructor that accepts `std::string` or `char const*`, you will be able to get away with a lot fewer constructors. – R Sahu Apr 05 '15 at 01:59
  • @RSahu it does! Does c++ do some kind of implicit type initialization? – Francisco Aguilera Apr 05 '15 at 01:59
  • The compiler will use at most one user defined conversion. See http://en.cppreference.com/w/cpp/language/cast_operator for additional details. – R Sahu Apr 05 '15 at 02:04
  • @RSahu Is this really considered a cast though? You're not casting string, char * types to uri, you're initializing a uri whose constructor takes those types... – Francisco Aguilera Apr 05 '15 at 02:06
  • The name of the URL is misleading. It talks about user defined conversions. Constructing an object of type `Type1` from an object of `Type2` where `Type1` has an appropriate constructor is under the same category. – R Sahu Apr 05 '15 at 02:08
  • The exact phrase is [Converting Constructor](http://en.cppreference.com/w/cpp/language/converting_constructor). – R Sahu Apr 05 '15 at 02:11
  • @RSahu Yeah, while I see the use of what you're saying, I can't see how it addresses my issue. Sure, in my definitions now I can delegate all overloaded constructor calls to the `client(uri::uri && host, uri::uri && auth)` constructor, but I am still left with a lot of overloads. This also brings up another issue, how do I delegate it to that constructor specifically, since, for example `client(const char * && host, const char * && auth) : client(host, auth){}` delegates to the string constructor by default instead of to the uri constructor? – Francisco Aguilera Apr 05 '15 at 02:19
  • 1
    Can you explain why you are accepting everything by non-const reference? (particularly the pointer versions). You don't seem to be changing the arguments. – M.M Apr 05 '15 at 03:31
  • 1
    If `uri` is a classname then `client(uri & host = uri(HOST_URI),` is illegal (non-const lvalue reference cannot bind to a temporary) – M.M Apr 05 '15 at 03:31
  • It was a copy mistake. Fixed. They are const. – Francisco Aguilera Apr 05 '15 at 05:00

3 Answers3

1

Since uri defines constructors that can take string as well as const char *, eliminate constructors whose parameters do not include those of uri type. This lets the user-defined conversion of uri implicitly convert those types for you.

#define HOST_URI "..."
#define AUTH_URI HOST_URI"..."

class client
{
private:
  uri host_;

  uri auth_;

public:
  client(const uri & host = uri(HOST_URI),
         const uri & auth = uri(AUTH_URI));

  client(const char * host = HOST_URI,
         const char * auth = AUTH_URI);
};

-

client::client(const uri::uri & host,
               const uri::uri & auth)
: host_(host), auth_(auth)
{
    ...
};

client::client(const char * host,
               const char * auth)
: client(uri::uri(host), uri::uri(auth)){}

"How do I delegate it to that constructor specifically?" Be explicit when using a delegating constructor.

client(const char * && host, const char * && auth) :
             client(uri(host), uri(auth)){}

or

client(const char * && host, const char * && auth) :
             client(string(host), string(auth)){}
Francisco Aguilera
  • 3,099
  • 6
  • 31
  • 57
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Ok, so that answers one question that your previous observation brought up :P However, I am still left with the original question. How to permute all combinations of constructor parameters, and some problems I am having due to the parameters being of rvalue reference type. – Francisco Aguilera Apr 05 '15 at 02:28
  • @FranciscoAguilera, my initial suggestion was to avoid `client` constructors with anything but `uri` as argument types. That will reduce the number of constructors you'll have to deal with. – R Sahu Apr 05 '15 at 02:31
  • Also, @Rsahu, `client c("", "");` gives an error: `no matching constructor for initialization of client`. So c++ is not performing the user-defined conversions for uri automatically like thought. – Francisco Aguilera Apr 05 '15 at 03:05
  • @FranciscoAguilera: Does `uri` have an implicit conversion from `const char*` or only from `std::string`? The C++ rules don't allow two implicit user-defined conversions in sequence on the same parameter. – Ben Voigt Apr 05 '15 at 06:06
1

How about a template:

#include <type_traits>

class client
{
    uri host_;
    uri auth_;

public:
    template <typename U, typename V,
              typename = typename std::enable_if<
                  std::is_constructible<uri, U&&>::value &&
                  std::is_constructible<uri, V&&>::value>::type>
    client(U && u, V && v)
    : host_(std::forward<U>(u))
    , auth_(std::forward<V>(v))
    { }

    // ...
};
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • To make the constructor a template, wouldn't I need to template the class? Making just the constructor a template doesn't make much sense... [See this post](http://stackoverflow.com/questions/3960849/c-template-constructor) – Francisco Aguilera Apr 05 '15 at 02:53
  • @FranciscoAguilera: You linked a post without actually reading the first two sentences of the answer, methinks. – Ben Voigt Apr 05 '15 at 03:34
  • @BenVoigt No i did read it, however, the code above yields the following error: `Constructor cannot have a return type`, which leads me to believe it can't be done with a constructor as I said. – Francisco Aguilera Apr 05 '15 at 03:35
  • 1
    @FranciscoAguilera: I think you may have misplaced a closing bracket. – Ben Voigt Apr 05 '15 at 04:37
  • 1
    @FranciscoAguilera: The code I posted does what I mean. [Demo](http://ideone.com/x9KNCN). I edited it, though, to make it more self-contained. – Kerrek SB Apr 05 '15 at 10:26
1

You can make a single class which can take all of those three things as parameter. Your post didn't include enough details for me to write a concrete answer, but here is pseudocode:

struct input_helper
{
    input_helper(uri &u);
    input_helper(string &u);
    input_helper(char *u);

    // data members to hold the inputs, maybe other processing to bring them to a common type
};

// the constructor
client(input_helper host, input_helper auth);
M.M
  • 138,810
  • 21
  • 208
  • 365
  • "You can make a single class which can take all of those three things as parameter." Matt, by doing this, aren't you just delegating the responsibility of creating these overloaded constructors to such class? – Francisco Aguilera Apr 05 '15 at 03:41
  • @FranciscoAguilera yes, but then you only have 3 constructors to write (which will all be simple), plus the main `client` constructor, instead of 9. – M.M Apr 05 '15 at 03:42
  • Hmm, seems so overcomplicated xD In reality I just needed to pass in 2 uri objects. But it simplifies my code everywhere else to be able to pass them in as these other types: `string` and `const char *` especially since such code would no longer need to include the header for uri. – Francisco Aguilera Apr 05 '15 at 03:46
  • If `uri` has a constructor that takes `const char *` and `string` too then you have no need for more than a single constructor taking two `uri` objects. – M.M Apr 05 '15 at 03:52
  • But I would have to explicitly change all of these instances of `const char *` and `string` parameters to client in my code by wrapping them in `uri` objects, and bringing in the uri header and namespace? Otherwise `client c("", "");` yields an error: `No matching constructor for initialization of 'client'. – Francisco Aguilera Apr 05 '15 at 03:57
  • It's a shame that there are no implicit conversions :) C# and Java spoiled me too much. – Francisco Aguilera Apr 05 '15 at 03:58
  • Anyways, I ended up doing it that way, but instead of including the header and namespace, I just made a typedef `client::uri_type` and am find and replacing all instances to be wrapped by that type. – Francisco Aguilera Apr 05 '15 at 04:02
  • If `uri` has a constructor taking `char const *` then you can write `client c("", "")`.. I'm not sure what you are talking about w.r.t C# and Java because in C++ you can define whatever conversions you like when you are making a class – M.M Apr 05 '15 at 04:19
  • Yeah but it doesn't, it has one that takes a `std::string` and c++ only allows 0 or 1 implicit conversions. `const char *` needing 2. `uri(std::string(const char *))` – Francisco Aguilera Apr 05 '15 at 04:21
  • add a constructor that takes `const char *` – M.M Apr 05 '15 at 04:21
  • which leads us back to what I was doing originally: Adding multiple overloads for all levels of implicit conversion above 1 ;) – Francisco Aguilera Apr 05 '15 at 04:25
  • 1
    You only need a single constructor added to uri – M.M Apr 05 '15 at 04:27
  • This whole thread would be a lot less vague if you posted the definition of `uri` and also fixed the code in your post that doesn't compile – M.M Apr 05 '15 at 04:28