|
/* Copyright (C) 2019 easyaspi314 (Devin). |
|
* Released under the MIT license. |
|
* |
|
* Permission is hereby granted, free of charge, to any person |
|
* obtaining a copy of this software and associated documentation |
|
* files (the "Software"), to deal in the Software without |
|
* restriction, including without limitation the rights to use, |
|
* copy, modify, merge, publish, distribute, sublicense, and/or |
|
* sell copies of the Software, and to permit persons to whom |
|
* the Software is furnished to do so, subject to the following |
|
* conditions: |
|
* |
|
* The above copyright notice and this permission notice shall |
|
* be included in all copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
* SOFTWARE. */ |
|
|
|
|
|
/* iostream.h: cin and cout, for C. |
|
* Requires a recent GCC or Clang version with support for C11, |
|
* __builtin_choose_expr, and _Generic, and preferrably it should |
|
* have a getline implementation, although it isn't required. |
|
* |
|
* Usage: See below. */ |
|
#define __STDC_WANT_LIB_EXT2__ 1 |
|
#include <ctype.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdint.h> |
|
|
|
// https://github.com/swansontec/map-macro |
|
#include "map.h" |
|
|
|
// Clears the data left over by scanf. |
|
static inline void _clear_stdin(void) |
|
{ |
|
int _c; |
|
while ((_c = getchar()) != '\n' && _c != EOF) |
|
; |
|
} |
|
|
|
/// A wrapper for fgets which also null terminates the string and doesn't |
|
/// leave anything in stdin. |
|
__attribute__((__unused__)) |
|
static int _length_getline(char *_x, size_t _length) |
|
{ |
|
if (_x == NULL || _length < 1) { |
|
return EOF; |
|
} |
|
|
|
memset(_x, 0, _length); |
|
|
|
// Skip whitespace and read the first character. |
|
if (scanf(" %c", _x) == EOF) { |
|
return EOF; |
|
} |
|
if (_length == 2) { |
|
return 1; |
|
} |
|
// Read starting at the first character because we just got it from |
|
// scanf. |
|
if (fgets(_x + 1, (int)_length - 1, stdin) == NULL) { |
|
return EOF; |
|
} |
|
|
|
// Find the newline in the string. Replace it with '\0' if it exists, |
|
// or clear out the line because there is data left over. |
|
char *_newline = strchr(_x, '\n'); |
|
if (_newline != NULL) { |
|
*_newline = '\0'; |
|
} else { |
|
// There is extra data, ignore it. |
|
_clear_stdin(); |
|
} |
|
return 1; |
|
} |
|
|
|
/// Dynamically allocates a buffer large enough to store the next line from stdin, null terminates it (removing '\n'), |
|
/// and stores it in _x. |
|
__attribute__((__unused__)) |
|
static int _malloc_getline(char **_x) |
|
{ |
|
#if !defined(IOSTREAM_DISABLE_GETLINE) \ |
|
&& (defined(__STDC_ALLOC_LIB__) /* C2x hyyype! */ \ |
|
|| (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200803L) || defined(_GNU_SOURCE) /* glibc */ \ |
|
|| defined(_WITH_GETLINE) /* BSD */ \ |
|
) |
|
if (_x == NULL) { |
|
return EOF; |
|
} |
|
|
|
char _tmp; |
|
// Skip whitespace until the first character. |
|
if (scanf(" %c", &_tmp) == EOF) { |
|
return EOF; |
|
} |
|
|
|
// We have to put that first one back and read again. |
|
// getline will (re)allocate memory and you can't just give an |
|
// offset pointer to realloc. |
|
ungetc(_tmp, stdin); |
|
|
|
// Set up *_x for getline |
|
*_x = NULL; |
|
size_t _size = 0; |
|
|
|
// Read the line |
|
if (getline(_x, &_size, stdin) == -1) { |
|
free(*_x); |
|
*_x = NULL; |
|
return EOF; |
|
} |
|
|
|
// Replace '\n' with '\0'. |
|
char *_newline = strchr(*_x, '\n'); |
|
if (_newline != NULL) { |
|
*_newline = '\0'; |
|
} |
|
return 1; |
|
|
|
#else // No getline. :( |
|
if (_x == NULL) { |
|
return EOF; |
|
} |
|
// 64 should be a decent size. |
|
int _size = 64; |
|
int _offset = 1; |
|
|
|
*_x = malloc((size_t)_size + 2); |
|
if (*_x == NULL) { |
|
return EOF; |
|
} |
|
// Skip whitespace and read the first character |
|
if (scanf(" %c", *_x) == EOF) { |
|
free(*_x); |
|
*_x = NULL; |
|
return EOF; |
|
} |
|
|
|
// Read in chunks starting at the first character. |
|
while (fgets((*_x) + _offset, _size + 1, stdin) != NULL) { |
|
char *_newline; |
|
// Find the newline and replace it if we can |
|
if ((_newline = strchr((*_x) + _offset, '\n')) != NULL) { |
|
*_newline = '\0'; |
|
return 1; |
|
} else if (feof(stdin)) { |
|
// We might be at the end. |
|
return 1; |
|
} else { |
|
// Grow the buffer by _size |
|
_offset += _size; |
|
char *_tmp = realloc(*_x, (size_t)(_size + _offset + 1)); |
|
if (_tmp == NULL) { |
|
free(*_x); |
|
*_x = NULL; |
|
return EOF; |
|
} |
|
*_x = _tmp; |
|
} |
|
} |
|
// shouldn't be able to get here but ok whatever |
|
return EOF; |
|
#endif // No getline |
|
} |
|
|
|
// Reads either "true" or "false" from stdin like std::boolalpha if _alpha |
|
// is true, or reads an integer and converts to bool. |
|
static int _read_bool(_Bool *_b, _Bool _alpha) |
|
{ |
|
if (_b == NULL) { |
|
return EOF; |
|
} |
|
if (_alpha) { |
|
char _buf[7]; |
|
// We read just enough to get a full word plus a space. |
|
if (scanf("%6s", _buf) == EOF) { |
|
return EOF; |
|
} |
|
if (strlen(_buf) == 5 && !strcmp(_buf, "false")) { |
|
*_b = 0; |
|
} else if (strlen(_buf) == 4 && !strcmp(_buf, "true")) { |
|
*_b = 1; |
|
} else { |
|
return EOF; |
|
} |
|
return 1; |
|
} else { |
|
long long val = 0; |
|
// Read into long long because it is the largest type we can have. |
|
// Otherwise, truncation might cause issues. |
|
if (scanf("%lli", &val) < 1) { |
|
return EOF; |
|
} |
|
|
|
// "cast to bool" |
|
*_b = !!val; |
|
return 1; |
|
} |
|
} |
|
|
|
// clang-format doesn't handle _Generic properly. |
|
// clang-format off |
|
|
|
// To make cin work with strict aliasing. |
|
union _iostream_pun { |
|
_Bool *_b; |
|
char *_c; |
|
char **_char_p; |
|
unsigned char *_hhu; |
|
signed char *_hhi; |
|
unsigned short *_hu; |
|
signed short *_hi; |
|
unsigned int *_u; |
|
signed int *_i; |
|
unsigned long *_lu; |
|
signed long *_li; |
|
unsigned long long *_llu; |
|
signed long long *_lli; |
|
float *_f; |
|
double *_lf; |
|
long double *_Lf; |
|
void *_p; |
|
}; |
|
|
|
|
|
/// See below. |
|
#define _cin_impl(x, len) __extension__ ({ \ |
|
union _iostream_pun _x = { ._p = &(x) }; \ |
|
int _tmp = _Generic((x), \ |
|
_Bool: _read_bool(_x._b, len), \ |
|
char: scanf(" %c", _x._c), \ |
|
signed char: scanf("%hhi", _x._hhi), \ |
|
unsigned char: scanf("%hhu", _x._hhu), \ |
|
signed short: scanf("%hi", _x._hi), \ |
|
unsigned short: scanf("%hu", _x._hu), \ |
|
signed int: scanf("%i", _x._i), \ |
|
unsigned int: scanf("%u", _x._u), \ |
|
signed long: scanf("%li", _x._li), \ |
|
unsigned long: scanf("%lu", _x._lu), \ |
|
signed long long: scanf("%lli", _x._lli), \ |
|
unsigned long long: scanf("%llu", _x._llu), \ |
|
float: scanf("%f", _x._f), \ |
|
double: scanf("%lf", _x._lf), \ |
|
long double: scanf("%Lf", _x._Lf), \ |
|
/* uintptr_t hides the "cast to pointer from smaller size" warning */ \ |
|
char **: _malloc_getline((char **)((uintptr_t)(x))), \ |
|
char *: _length_getline((char *)((uintptr_t)(x)), len /* bufsize */) \ |
|
); \ |
|
/* set the value to zero on errors like std::cin. */ \ |
|
if (_tmp < 1) { \ |
|
_Generic((x), \ |
|
char *: (void)0, char **: (void)0, default: (void)memset(_x._p, 0, sizeof(x)) \ |
|
); \ |
|
} \ |
|
/* Don't clear when we have char * or char ** as those do that automatically. */ \ |
|
_Generic((x), char *: (void)0, char **: (void)0, default: _clear_stdin()); \ |
|
_tmp; \ |
|
}) |
|
|
|
|
|
/// int cin(T x, [size_t len|bool boolalpha]): |
|
/// Reads a line from stdin into x using _Generic to determine the type. |
|
/// For char *, the len argument is required and it sets the limit to how long the input |
|
/// will be including a null terminator. |
|
/// For both char * and char **, the string will be null terminated, with any newlines |
|
/// removed. |
|
/// |
|
/// Usage: |
|
/// // Basic arithmetic types can be read like so: |
|
/// {int, char, signed char, unsigned long long, float, etc} x; |
|
/// cin(x); |
|
/// |
|
/// // Truncated strings pass the length (including the null terminator) as a second argument. |
|
/// char str[21]; |
|
/// cin(str, sizeof(str)); |
|
/// |
|
/// // A dynamically allocated line will be recieved like so |
|
/// char *buf = NULL; |
|
/// char **tmp = &buf; |
|
/// cin(tmp); |
|
/// // don't forget to free! |
|
/// |
|
/// Caveats: |
|
/// - To read a character, use 'char'. To read an 8-bit signed integer, use 'signed char', |
|
/// and for an 8-bit unsigned integer, use 'unsigned char'. |
|
/// - Hex and octal are only detected with signed variables, due to the limitations of |
|
/// scanf. |
|
/// - As shown above, you need a temporary variable set to the address of a null char |
|
/// pointer to get the automatic memory allocation to work. The buffer must be freed. |
|
/// |
|
/// Returns EOF on an error, or anything else otherwise. |
|
#define cin(...) _cin_impl_wrap(__VA_ARGS__, 0, 0) |
|
|
|
/// Wrapper for cin that prevents variadic macro warnings. |
|
#define _cin_impl_wrap(x, len, ...) _cin_impl(x, len + 0) |
|
|
|
|
|
#define boolalpha 1 |
|
/// For those who prefer it. |
|
#define endl ((char)'\n') |
|
|
|
/// Attempts to use lexical parsing to tell whether an int is a char literal or not. |
|
/// See below for how it works. |
|
#define _is_char_literal(x) (sizeof(#x) > 3 && ((#x)[0] == '\'' || (#x)[sizeof(#x) - 2] == '\'')) |
|
|
|
/// Prints x to the console. See cout below. |
|
#define _cout_impl(x) __extension__({ \ |
|
/* typeof doesn't decay array to pointer. We can however use __builtin_choose_expr \ |
|
* to either select the type of the argument or const char *. */ \ |
|
__typeof__( \ |
|
__builtin_choose_expr(_Generic((x), char *: 0, const char *: 0, default: 1), \ |
|
(x), \ |
|
(const char *)NULL) \ |
|
) _x = (x); \ |
|
/* XXX: Does this violate strict aliasing? GCC doesn't warn about it. */ \ |
|
const void *_val = &_x; \ |
|
_Generic((x), \ |
|
_Bool: fputs((*(const _Bool *)(_val)) ? "true" : "false", stdout), \ |
|
char: putchar(*(const char *)(_val)), \ |
|
signed char: printf("%hhi", *(const signed char *)(_val)), \ |
|
unsigned char: printf("%hhu", *(const unsigned char *)(_val)), \ |
|
signed short: printf("%hi", *(const signed short *)(_val)), \ |
|
unsigned short: printf("%hu", *(const unsigned short *)(_val)), \ |
|
signed int: (_is_char_literal(x) ? putchar(*(const signed int *)(_val)) \ |
|
: printf("%i", *(const signed int *)(_val))), \ |
|
unsigned int: (_is_char_literal(x) ? putchar(*(const signed int *)(_val)) \ |
|
: printf("%u", *(const unsigned int *)(_val))), \ |
|
signed long: printf("%li", *(const signed long *)(_val)), \ |
|
unsigned long: printf("%lu", *(const unsigned long *)(_val)), \ |
|
signed long long: printf("%lli", *(const signed long long *)(_val)), \ |
|
unsigned long long: printf("%llu", *(const unsigned long long *)(_val)), \ |
|
float: printf("%f", (const double)*(const float *)(_val)), \ |
|
double: printf("%lf", *(const double *)(_val)), \ |
|
long double: printf("%Lf", *(const long double *)(_val)), \ |
|
char *: fputs((const char *)((const uintptr_t)(_x)), stdout), \ |
|
const char *: fputs((const char *)((const uintptr_t)(_x)), stdout), \ |
|
void *: printf("%p", (const void *)(const uintptr_t)(x)) \ |
|
); \ |
|
}) |
|
|
|
/// Expand the cout statement into a ternary that will avoid calling further _cout_impls in |
|
/// the case of an error. |
|
#define _cout_expand(x) (_cout_impl(x) == EOF) ? EOF : |
|
|
|
/// int cout(...): |
|
/// Prints one or more items to stdout, using _Generic to deduce the proper types |
|
/// Usage: |
|
/// {int, char, unsigned int, const char *} x = ...; |
|
/// cout(x, cin_endl); |
|
/// |
|
/// cout("Hello world"); |
|
/// |
|
/// Note: Because character literals are int by default, detection of these is a little |
|
/// hacky. |
|
/// If the literal LEXICALLY begins or ends in a single quote (') or is manually cast |
|
/// to char, it will use putchar. As in, the actual macro parameters are stringified and |
|
/// checked. |
|
/// |
|
/// cout('a'); // a |
|
/// cout(('a')); // 97 |
|
/// cout((char)('a')); // a |
|
/// cout('a' + 1); // b |
|
/// cout(1 + 'a'); // b |
|
/// cout(1 + 'a' + 2); // 100 |
|
/// int c = 'a'; |
|
/// cout(c); // 97 |
|
/// cout((char)c); // a |
|
/// |
|
/// Returns: |
|
#define cout(...) __extension__ ({ MAP(_cout_expand, __VA_ARGS__) EOF; }) |
|
// clang-format on |
|
|
|
#ifdef IOSTREAM_DEMO |
|
#include <stdbool.h> |
|
// Just a little demo/test. |
|
int main(void) |
|
{ |
|
const char *space1 = " "; |
|
char space2[] = " "; |
|
|
|
// should print Hello 1 2 3 |
|
cout((char)72, 'd' + 1, 3 + 'i', "l", 'o', ' ', 0x1, space1, 2ll, space2, (unsigned short)3u, endl); |
|
int x; |
|
cout("int: "); |
|
cin(x); |
|
cout(x, endl); |
|
bool b; |
|
cout("bool (nonzero): "); |
|
cin(b); |
|
cout(b, endl); |
|
cout("bool (\"true\" or \"false\"): "); |
|
cin(b, boolalpha); |
|
cout(b, endl); |
|
char y; |
|
cout("char: "); |
|
cin(y); |
|
cout(y, endl); |
|
char buf[21]; |
|
cout("string (20 max): "); |
|
cin(buf, sizeof(buf)); |
|
cout(buf, endl); |
|
char* buf_alloc = NULL; |
|
char** buf_alloc_p = &buf_alloc; |
|
cout("string: (any length): "); |
|
cin(buf_alloc_p); |
|
cout(buf_alloc, endl); |
|
free(buf_alloc); |
|
float f; |
|
cout("float: "); |
|
cin(f); |
|
cout(f, endl); |
|
} |
|
#endif |