Open renbaoshuo opened 1 year ago
确实,不然 testlib 不能用 readInts 这种东西还挺难受的
很赞成,而且老版 testlib 有个 BUG(一下子找不到对应的链接了)。目前 UOJ 管理员传题,也多是使用自己上传的新版 testlib。
主要难点如下:
感觉一种可能的方法是像 Testlib-for-Lemons 一样开个类似的 “Testlib-for-uoj” 库,然后和上游 MikeMirzayanov/testlib 保持一种同步,不过代价是可能要稍微改一下 include 结构,因为 git 的 submodule 只能加目录。
以下是 UOJ 目前使用的 testlib.h
与 testlib 0.9.5 版之间的 diff:
--- testlib-0.9.5/testlib.h
+++ judger/uoj_judger/include/testlib.h
@@ -120,12 +120,20 @@ const char* latestFeatures[] = {
#endif
/* Overrides random() for Borland C++. */
+#ifdef __EMSCRIPTEN__
+#define srand __srand_deprecated
+#define rand __rand_deprecated
+#endif
#define random __random_deprecated
#include <stdlib.h>
#include <cstdlib>
#include <climits>
#include <algorithm>
#undef random
+#ifdef __EMSCRIPTEN__
+#undef rand
+#undef srand
+#endif
#include <cstdio>
#include <cctype>
@@ -140,6 +148,10 @@ const char* latestFeatures[] = {
#include <fcntl.h>
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+
#if ( _WIN32 || __WIN32__ || _WIN64 || __WIN64__ )
# if !defined(_MSC_VER) || _MSC_VER>1400
# include <windows.h>
@@ -152,6 +164,10 @@ const char* latestFeatures[] = {
# define WORD unsigned short
#endif
+#ifdef linux
+#include <unistd.h>
+#endif
+
#ifndef LLONG_MIN
#define LLONG_MIN (-9223372036854775807LL - 1)
#endif
@@ -235,8 +251,8 @@ const char* latestFeatures[] = {
#else
# define NORETURN
#endif
-
-static char __testlib_format_buffer[16777216];
+
+static char __testlib_format_buffer[1024];
static int __testlib_format_buffer_usage_count = 0;
#define FMT_TO_RESULT(fmt, cstr, result) std::string result; \
@@ -314,21 +330,17 @@ static bool __testlib_isNaN(double r)
std::memcpy((void*)&llr1, (void*)&ra, sizeof(double));
ra = -ra;
std::memcpy((void*)&llr2, (void*)&ra, sizeof(double));
- long long llnan = 0xFFF8000000000000;
+ long long llnan = 0xFFF8000000000000ll;
return __testlib_prelimIsNaN(r) || llnan == llr1 || llnan == llr2;
}
static double __testlib_nan()
{
__TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long));
-#ifndef NAN
- long long llnan = 0xFFF8000000000000;
+ long long llnan = 0xFFF8000000000000ll;
double nan;
std::memcpy(&nan, &llnan, sizeof(double));
return nan;
-#else
- return NAN;
-#endif
}
static bool __testlib_isInfinite(double r)
@@ -351,6 +363,23 @@ static void __testlib_set_binary(std::FI
#endif
}
+static FILE *__testlib_fopen(const char *name, const char *mode)
+{
+#ifdef __EMSCRIPTEN__
+ EM_ASM(
+ try {
+ FS.stat('/cwd');
+ } catch (e) {
+ FS.mkdir('/cwd');
+ FS.mount(NODEFS, { root: '.' }, '/cwd');
+ }
+ );
+ return fopen((std::string("/cwd/") + name).c_str(), mode);
+#else
+ return fopen(name, mode);
+#endif
+}
+
/*
* Very simple regex-like pattern.
* It used for two purposes: validation and generation.
@@ -1629,8 +1658,8 @@ struct InStream
/* As "readInteger()" but ensures that value in the range [minv,maxv]. */
int readInteger(int minv, int maxv, const std::string& variableName = "");
/* As "readInt()" but ensures that value in the range [minv,maxv]. */
- int readInt(int minv, int maxv, const std::string& variableName = "");
-
+
+ int readInt(int minv, int maxv, const std::string& variableName = "");
/*
* Reads new double. Ignores white-spaces into the non-strict mode
* (strict mode is used in validators usually).
@@ -1814,6 +1843,8 @@ InStream::InStream()
InStream::InStream(const InStream& baseStream, std::string content)
{
+ file = (FILE*)0xbadfeed;
+ stdfile = false;
reader = new StringInputStreamReader(content);
opened = true;
strict = baseStream.strict;
@@ -1864,6 +1895,34 @@ void InStream::textColor(WORD color)
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle, color);
#endif
+
+#ifdef linux
+ char *shell_path = getenv("SHELL");
+ if (shell_path && strcmp(shell_path, "/bin/bash") == 0 && isatty(2))
+ {
+ switch (color)
+ {
+ case 0:
+ fprintf(stderr, "\e[0m");
+ break;
+ case LightRed:
+ fprintf(stderr, "\e[0;31m");
+ break;
+ case LightGreen:
+ fprintf(stderr, "\e[0;32m");
+ break;
+ case LightYellow:
+ fprintf(stderr, "\e[0;33m");
+ break;
+ case LightCyan:
+ fprintf(stderr, "\e[0;36m");
+ break;
+ case LightGray:
+ fprintf(stderr, "\e[0m");
+ break;
+ }
+ }
+#endif
}
NORETURN void halt(int exitCode)
@@ -1874,6 +1933,9 @@ NORETURN void halt(int exitCode)
std::fprintf(stderr, "Exit code: %d\n", exitCode);
InStream::textColor(InStream::LightGray);
#endif
+#ifdef linux
+ InStream::textColor(0);
+#endif
std::exit(exitCode);
}
@@ -1945,7 +2007,7 @@ NORETURN void InStream::quit(TResult res
if (resultName != "")
{
- resultFile = std::fopen(resultName.c_str(), "w");
+ resultFile = __testlib_fopen(resultName.c_str(), "w");
if (resultFile == NULL)
quit(_fail, "Can not write to the result file");
if (appesMode)
@@ -2066,7 +2128,7 @@ void InStream::reset()
close();
if (!stdfile)
- if (NULL == (file = std::fopen(name.c_str(), "rb")))
+ if (NULL == (file = __testlib_fopen(name.c_str(), "rb")))
{
if (mode == _output)
quits(_pe, std::string("File not found: \"") + name + "\"");
@@ -2085,8 +2147,17 @@ void InStream::reset()
void InStream::init(std::string fileName, TMode mode)
{
opened = false;
- name = fileName;
- stdfile = false;
+ if (fileName == "/dev/stdin")
+ {
+ name = "stdin";
+ this->file = stdin;
+ stdfile = true;
+ }
+ else
+ {
+ name = fileName;
+ stdfile = false;
+ }
this->mode = mode;
reset();
}
@@ -2524,24 +2595,24 @@ int InStream::readInt()
return readInteger();
}
-int InStream::readInt(int minv, int maxv, const std::string& variableName)
+int InStream::readInt(int minv, int maxv, const std::string &variableName)
{
int result = readInt();
if (result < minv || result > maxv)
- {
- if (variableName.empty())
- quit(_wa, ("Integer " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
- else
- quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
- }
+ {
+ if (variableName.empty())
+ quit(_wa, ("Integer " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+ else
+ quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+ }
return result;
}
int InStream::readInteger(int minv, int maxv, const std::string& variableName)
{
- return readInt(minv, maxv, variableName);
+ return readInt(minv, maxv, variableName.c_str());
}
double InStream::readReal()
@@ -2659,7 +2730,7 @@ bool InStream::eoln()
{
bool returnCr = false;
-#ifdef ON_WINDOWS
+#ifdef CR_MUST_IN_EOL
if (c != CR)
{
reader->unreadChar(c);
@@ -2955,18 +3026,6 @@ void registerGen(int argc, char* argv[])
registerGen(argc, argv, 0);
}
#else
-#ifdef __GNUC__
- __attribute__ ((deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)."
- " The third parameter stands for the random generator version."
- " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)."
- " Version 1 has been released on Spring, 2013. Use it to write new generators.")))
-#endif
-#ifdef _MSC_VER
- __declspec(deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)."
- " The third parameter stands for the random generator version."
- " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)."
- " Version 1 has been released on Spring, 2013. Use it to write new generators."))
-#endif
void registerGen(int argc, char* argv[])
{
std::fprintf(stderr, "Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)."
@@ -3022,9 +3081,12 @@ void registerInteraction(int argc, char*
inf.init(argv[1], _input);
- tout.open(argv[2], std::ios_base::out);
- if (tout.fail() || !tout.is_open())
- quit(_fail, std::string("Can not write to the test-output-file '") + argv[2] + std::string("'"));
+ if (strcmp(argv[2], "stdout") != 0)
+ {
+ tout.open(argv[2], std::ios_base::out);
+ if (tout.fail() || !tout.is_open())
+ quit(_fail, std::string("Can not write to the test-output-file '") + argv[2] + std::string("'"));
+ }
ouf.init(stdin, _output);
@@ -3228,9 +3290,6 @@ void shuffle(_RandomAccessIter __first,
template<typename _RandomAccessIter>
-#ifdef __GNUC__
-__attribute__ ((error("Don't use random_shuffle(), use shuffle() instead")))
-#endif
void random_shuffle(_RandomAccessIter , _RandomAccessIter )
{
quitf(_fail, "Don't use random_shuffle(), use shuffle() instead");
@@ -3242,9 +3301,6 @@ void random_shuffle(_RandomAccessIter ,
# define RAND_THROW_STATEMENT
#endif
-#ifdef __GNUC__
-__attribute__ ((error("Don't use rand(), use rnd.next() instead")))
-#endif
int rand() RAND_THROW_STATEMENT
{
quitf(_fail, "Don't use rand(), use rnd.next() instead");
@@ -3253,12 +3309,6 @@ int rand() RAND_THROW_STATEMENT
//throw "Don't use rand(), use rnd.next() instead";
}
-#ifdef __GNUC__
-__attribute__ ((error("Don't use srand(), you should use "
- "'registerGen(argc, argv, 1);' to initialize generator seed "
- "by hash code of the command line params. The third parameter "
- "is randomGeneratorVersion (currently the latest is 1).")))
-#endif
void srand(unsigned int seed) RAND_THROW_STATEMENT
{
quitf(_fail, "Don't use srand(), you should use "
当前 UOJ 使用的 testlib 版本为
0.9.5
,而 MikeMirzayanov/testlib 中的版本已经到达了0.9.40
,建议升级一下以适配基于新版 testlib 编写的 checker / validator / ...。