我正在为自己的CP比赛使用的小型调试工具编写代码。我已经在here上讨论了同一主题,讨论了几个星期。我知道交叉发布的疑问是一个实际问题,但是我真正想要的只是对该主题的更多看法。意见越多越好(PS:这是我的第一个交叉帖子)。
因此,我在比赛中的各种CP专家代码中都看到了漂亮的调试宏,但是我确实看到了一个流(在其他论坛上进行了全面的讨论),流是这样做的:
Debug () << Perks;
并包含一个名为“ CppDebug.h”的头文件(或类似名称,我无法回忆起),该文件产生了以下结果:
// The solution to the problem followed by:
.
.
[LINE : 27] "Perks" created : 175
[LINE : 29] "Perks" : 200
[LINE : 85] "Perks" : 225 (loop iteration 1)
[LINE : 85] "Perks" : 250 (loop iteration 2)
[LINE : 85] "Perks" : 275 (loop iteration 3)
[LINE : 85] "Perks" : 300 (loop iteration 4)
[LINE : 85] "Perks" : 400 (loop iteration 12)
[LINE : 99] "Perks" : 400 (std::cout)
[LINE : 140] "Perks" deleted : 400
在这里我会同意,我不确定我是否看到了std :: cout和删除的部分(因为我记不清楚要去重新观看流的流媒体的输出或名称了),但是我确定行号和值的历史记录的显示方式与上面的示例类似。在那个时候,我不是很擅长编程,还只是非常基础的新东西,所以我对自己想,一旦我学到更多,实现起来可能很容易,但是事实证明,这非常令人困惑。至少是我。
我想对如何使Debugger类在处理用户定义的类型以及对内置类型的完全控制方面更具通用性和灵活性提出意见。这是我到目前为止编写的代码:
#ifndef Z_DEBUG_H_INCLUDED
#define Z_DEBUG_H_INCLUDED
#include <bits/stdc++.h>
#define Debug(VarArgs...) \
{ \
std::string Str = #VarArgs; \
std::replace (Str.begin () , Str.end () , ',' , ' '); \
Str += " Debugging..."; \
std::stringstream SS (Str); std::istream_iterator<std::string> Iterator (SS); \
\
std::cerr << std::endl << std::endl \
<< "[FILE] : " << __FILE__ << '\n' \
<< "[DATE] : " << __DATE__ << '\n' \
<< "[TIME] : " << __TIME__ << '\n'; \
\
Error (Iterator , VarArgs); \
}
void Error (std::istream_iterator <std::string> Iterator)
{ std::cerr << '\n' << *Iterator; }
template <typename Type , typename... Args> void Error (std::istream_iterator <std::string> Iterator , const Type& Var , Args... ExtraArgs)
{
std::cerr << "\nValues of [" << *Iterator << "] : " << Var << '\n';
Error (++Iterator , ExtraArgs...);
}
template <typename T> class Debugger
{
private:
T Variable;
std::vector <T> VariableHistory;
public:
Debugger ();
Debugger (const Debugger <T>& Other);
Debugger (const T& Other);
~Debugger ();
operator T () const;
Debugger <T>& operator = (const Debugger <T>& Other);
Debugger <T>& operator += (const Debugger <T>& Other);
Debugger <T>& operator -= (const Debugger <T>& Other);
Debugger <T>& operator *= (const Debugger <T>& Other);
Debugger <T>& operator /= (const Debugger <T>& Other);
Debugger <T>& operator &= (const Debugger <T>& Other);
Debugger <T>& operator |= (const Debugger <T>& Other);
Debugger <T>& operator %= (const Debugger <T>& Other);
Debugger <T>& operator ^= (const Debugger <T>& Other);
Debugger <T>& operator <<= (const Debugger <T>& Other);
Debugger <T>& operator >>= (const Debugger <T>& Other);
Debugger <T>& operator ++ ();
Debugger <T>& operator -- ();
Debugger <T> operator ++ (int);
Debugger <T> operator -- (int);
template <typename Ty> friend std::istream& operator >> (std::istream& Stream , Debugger <Ty>& Input);
template <typename Ty> friend std::ostream& operator << (std::ostream& Stream , const Debugger <Ty>& Output);
template <typename Tp1 , typename Tp2> friend std::ostream& operator << (std::ostream& Stream , const Debugger <std::pair <Tp1 , Tp2>>& Output);
};
template <typename T> Debugger <T>::Debugger ()
: Variable ()
, VariableHistory ({Variable})
{ }
template <typename T> Debugger <T>::Debugger (const Debugger <T>& Other)
: Variable (Other.Variable)
, VariableHistory (Other.VariableHistory)
{ }
template <typename T> Debugger <T>::Debugger (const T& Other)
: Variable (Other)
, VariableHistory ({Variable})
{ }
template <typename T> Debugger <T>::~Debugger ()
{ }
template <typename T> Debugger <T>::operator T () const
{ return Variable; }
template <typename T> Debugger <T>& Debugger <T>::operator = (const Debugger <T>& Other)
{
Variable = Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator += (const Debugger <T>& Other)
{
Variable += Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator -= (const Debugger <T>& Other)
{
Variable -= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator *= (const Debugger <T>& Other)
{
Variable *= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator /= (const Debugger <T>& Other)
{
Variable /= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator &= (const Debugger <T>& Other)
{
Variable &= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator |= (const Debugger <T>& Other)
{
Variable |= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator %= (const Debugger <T>& Other)
{
Variable %= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator ^= (const Debugger <T>& Other)
{
Variable ^= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator <<= (const Debugger <T>& Other)
{
Variable <<= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator >>= (const Debugger <T>& Other)
{
Variable >>= Other.Variable;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator ++ ()
{
Variable++;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T>& Debugger <T>::operator -- ()
{
Variable--;
VariableHistory.push_back (Variable);
return *this;
}
template <typename T> Debugger <T> Debugger <T>::operator ++ (int)
{
Debugger <T> Copy (*this);
Variable++;
VariableHistory.push_back (Variable);
return Copy;
}
template <typename T> Debugger <T> Debugger <T>::operator -- (int)
{
Debugger <T> Copy (*this);
Variable--;
VariableHistory.push_back (Variable);
return Copy;
}
template <typename Ty> std::istream& operator >> (std::istream& Stream, Debugger <Ty>& Input)
{
Stream >> Input.Variable;
Input.VariableHistory.push_back (Input.Variable);
return Stream;
}
template <typename Ty> std::ostream& operator << (std::ostream& Stream, const Debugger <Ty>& Output)
{
Stream << '\n';
for (size_t i = 0; i < Output.VariableHistory.size (); i++)
Stream << "\n" << Output.VariableHistory [i];
return Stream;
}
template <typename Tp1 , typename Tp2> std::ostream& operator << (std::ostream& Stream , const Debugger <std::pair <Tp1 , Tp2>>& Output)
{
Stream << '\n';
for (size_t i = 0; i < Output.VariableHistory.size (); i++)
Stream << "\n{" << Output.VariableHistory[i].first << " , " << Output.VariableHistory[i].second << "}";
return Stream;
}
template <typename Tp1 , typename Tp2> std::ostream& operator << (std::ostream& Stream , const std::pair <Tp1 , Tp2>& Pair)
{
Stream << "{" << Pair.first << " , " << Pair.second << "}";
return Stream;
}
#endif // Z_DEBUG_H_INCLUDED
我的CP代码模板为:
#include <bits/stdc++.h>
#if 1
#include "z_debug.h"
#define char Debugger <char>
#define int32 Debugger <int>
#define uint32 Debugger <unsigned int>
#define int64 Debugger <long long>
#define uint64 Debugger <unsigned long long>
#define size_t Debugger <size_t>
#define float Debugger <float>
#define double Debugger <double>
#else
#define int32 int
#define uint32 unsigned int
#define int64 long long
#define uint64 unsigned long long
#define Debug(...)
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// A lot of other things but no point posting it....
int main (void)
{
cout.sync_with_stdio (false);
cin.tie (nullptr);
cout.precision (10);
cout << fixed;
return 0;
}
来源:main的前四行是从Gennady Korotkevich的CP代码模板复制而来的。其他一切都是自写的。
我想问的重要问题:
如何追溯到赋值运算符重载的调用方以“知道”从何处调用重载?
有人可以建议一种更好的方法吗?
我如何使我的Debugger类适用于几乎所有类型?能做到吗(我相信是的,因为程序员可以做任何事情:))
除了要为其提供运算符重载的赋值操作外,我希望Debugger类型的实例的行为与T类型完全相同(我做对了吗?)。我希望前者具有后者的所有属性,减去在CP代码模板的开头使用“ if 1”时要调用Debugger方法的赋值操作。正确的做法是什么?
我想让Debugger类做什么?
感谢您的时间和耐心!