9

I don't know if it's possible but I want to do stuff like

int someval = 1;
if({1,2,3,4}_v.contains(someval ))

but when I try to define literal as:

std::vector<int> operator"" _v ( std::initializer_list<int> t )
{
    return std::vector<int> (t);
}

to accept initializer list of ints I get

 error: 'std::vector<int> operator"" _v(std::initializer_list<int> t)' has invalid argument list

Is there a way to do this? What I really want is to finally be rid of stuff like

if(value == 1 || value ==2 || value == 3 ...

Having to write stuff like this is really annoying, because you'd expect syntax to be

if value in (value1, value2 ...) 

or something similar.

Zeks
  • 2,265
  • 20
  • 32
  • Out of interest, why `v`? Presumably as it stands for "vector"? – Lightness Races in Orbit Jan 16 '13 at 21:52
  • 1
    Yup, can be anything. I just want to have shorter if notation for multiple cases – Zeks Jan 16 '13 at 21:52
  • 1
    Use C++17 and a fold expression over `||`; see [this excellent answer](https://stackoverflow.com/a/15181949/2757035) to a similar question. – underscore_d Sep 19 '18 at 21:13
  • Frankly, I consider the example in that answer ugly. The thing you compare shouldn't live in the same unseparated context as the things you compare to. Thankfully, fold expressions can be use a bit smarter to achieve what is needed with more clarity, there was a talk about that during C++ Russia by Björn Fahller. You can see the footnotes here: https://assets.ctfassets.net/oxjq45e8ilak/7lP3ueTFEJnmdVjGGjrgHI/e30dfcbff0630fb63389f3f3e734a1a3/100644_2039674689_Bjrn_Fahller_Modern_techniques_for_keeping_your_code_dry.pdf – Zeks Dec 04 '19 at 10:11

3 Answers3

8

How about this:

#include <initializer_list>

template <typename T>
bool contains(std::initializer_list<T> const & il, T const & x)
{
    for (auto const & z : il) { if (z == x) return true; }
    return false;
}

Usage:

bool b = contains({1, 2, 3}, 5);  // false
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    Personally, I would just use `std::find`, rather than the loop. – Dave S Jan 16 '13 at 22:08
  • Yes, this notation is the closest we can get it seems. Still too much braces but much easier to read than what is usually used. – Zeks Jan 16 '13 at 23:05
6

you'd expect syntax to be

if value in (value1, value2 ...) 

or something similar.

If you're willing to add one extra character, try this syntax:

#include <algorithm>
#include <iostream>
#include <array>

template <typename T0, typename T1, std::size_t N>
bool operator *(const T0& lhs, const std::array<T1, N>& rhs) {
  return std::find(begin(rhs), end(rhs), lhs) != end(rhs);
}

template<class T0, class...T> std::array<T0, 1+sizeof...(T)> in(T0 arg0, T...args) {
  return {{arg0, args...}};
}

int main () {
  if( 2 *in(1,2,3) ) { std::cout << "Hello\n"; }
  if( 4 *in(5,6,7,8) ) { std::cout << "Goodbye\n"; }
}
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Whoa, now that's creative use of new stuff :) – Zeks Jan 17 '13 at 08:46
  • On a side note - what did you read to gain such deep understanding of this stuff? I can't wrap it in my head enough to start using it. When I carefully read the code - it makes sense, but I can't bring myself in a state of mind to actually start writing things like that. – Zeks Mar 03 '13 at 20:57
  • For general knowledge, I've been reading and writing C (and later C++) for 30 years. For templates specifically, StackOverflow, the C++ standard, and Google (in that order). – Robᵩ Mar 04 '13 at 02:06
  • If someone is a custom syntax freak, which is probably not that good, they can `#define in *in_impl` ;) – cubuspl42 May 08 '13 at 17:12
  • Nah, the original answer works just fine. :) No need to #define anything. Been using this past few months – Zeks Aug 04 '13 at 17:35
4

§13.5.8/3 says:

The declaration of a literal operator shall have a parameter-declaration-clause equivalent to one of the following:

const char*
unsigned long long int
long double
char
wchar_t
char16_t
char32_t
const char*, std::size_t
const wchar_t*, std::size_t
const char16_t*, std::size_t
const char32_t*, std::size_t

So it looks like you can't have a parameter of initializer_list type.

I can only think of the obvious as an alternative; if you don't mind typing a little more you can do something like

std::vector<int> v(std::initializer_list<int> l) {
    return { l };
}

int someval = 1;
if(v({1,2,3,4}).contains(someval))

Alternatively you could get wacky and write an operator overload for initializer_list (haven't tested though):

bool operator<=(std::intializer_list<int> l, int value) {
    return std::find(std::begin(l), std::end(l), value) != std::end(l);
}

And

if ({1, 2, 3, 4} <= 3)

should work...

Actually nevermind, it doesn't. You'll have to go with a normal function.

Community
  • 1
  • 1
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Bummer :( and I so hoped taht c++11 could allow a replacement for if( val == 1 || val == 2 ||...) – Zeks Jan 16 '13 at 21:56
  • Okay but what is the solution?! I can't think of anything better than tokenizing `"1,2,3,4"` – Lightness Races in Orbit Jan 16 '13 at 21:57
  • Also, what the heck is that text doing under §13.5! I would have expected it under §2.14.8, myself. – Lightness Races in Orbit Jan 16 '13 at 21:58
  • @LightnessRacesinOrbit well the question was is there a way to do the literal syntax, and the answer is no, but I'll add some alternative if I can think of a good one. – Seth Carnegie Jan 16 '13 at 21:59
  • @LightnessRacesinOrbit well I think I've seen something for compile-time string manipulations so tokenising `"1,2,3,4"` is probably feasible, but probably more trouble than it's worth. – Seth Carnegie Jan 16 '13 at 22:01
  • Is this a fundamental limitation, or simply not implemented for current standard? – Zeks Jan 16 '13 at 22:04
  • Btw, I don't mind typing a little more. Thx very much, this will simplify stuff a lot. Should have though about it :) – Zeks Jan 16 '13 at 22:06
  • Operator overload is nice but I am afraid this will hurt readability quite a lot. I mean - for ppl who will have to read the code other than me – Zeks Jan 16 '13 at 22:09
  • @Zeks It's just not implemented for the current standard, I don't think there's a reason why besides time. And you can choose another operator if you think another is more readable, like `<<` or something. – Seth Carnegie Jan 16 '13 at 22:11
  • The operator overload way doesn't work by the way. Plain if({1,2,3,4} << 1) fails with "error: expected primary-expression before '{' token" – Zeks Jan 16 '13 at 22:30
  • As it seems, whatever it casts {1,2,3,4} to is not std::initializer_list, because explicitly qualifying it as such works – Zeks Jan 16 '13 at 22:36
  • @Zeks ah, sorry, I didn't test it, I assumed it should have worked. – Seth Carnegie Jan 16 '13 at 22:42
  • 1
    Kerrek's post from below seems to be the shortest and most natural notation for the task then. – Zeks Jan 16 '13 at 22:44