new feature
This commit is contained in:
77
mrw/deque.hpp
Normal file
77
mrw/deque.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__DEQUE__HPP__
|
||||
#define __MRW__DEQUE__HPP__
|
||||
|
||||
#include <deque>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/string.hpp>
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief push a value to a deque
|
||||
|
||||
@code
|
||||
std::deque<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
@endcode
|
||||
|
||||
@param l a deque of values
|
||||
@param o a value to be inserted into deque @c l
|
||||
@pre #include <mrw/deque.hpp>
|
||||
*/
|
||||
template <typename T, typename A>
|
||||
std::deque<T, A>& operator<<(std::deque<T, A>& l, const T& o) throw(std::bad_exception) {
|
||||
l.push_back(o);
|
||||
return l;
|
||||
}
|
||||
|
||||
/** @brief extract the first value of a deque
|
||||
|
||||
@code
|
||||
std::deque<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
test>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 test=={5, 6, 7, 8}
|
||||
@endcode
|
||||
|
||||
@param l a deque of values
|
||||
@param o a value to be extracted from deque @c l
|
||||
@throw mrw::length_error, if deque was empty on entry
|
||||
@note when something is extracted from a deque, it is removed
|
||||
from the deque, that means after every shift the deque is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
template <typename T, typename A>
|
||||
std::deque<T, A>& operator>>(std::deque<T, A>& l, T& o) throw(std::exception) {
|
||||
typename std::deque<T, A>::iterator it(l.begin());
|
||||
if (it==l.end())
|
||||
throw mrw::length_error(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::deque<>& operator>>(std::deque<>&, T&),"
|
||||
" deque is empty");
|
||||
o = *it;
|
||||
l.erase(it);
|
||||
return l;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
77
mrw/list.hpp
Normal file
77
mrw/list.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__LIST__HPP__
|
||||
#define __MRW__LIST__HPP__
|
||||
|
||||
#include <list>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/string.hpp>
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief push a value to a list
|
||||
|
||||
@code
|
||||
std::list<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
@endcode
|
||||
|
||||
@param l a list of values
|
||||
@param o a value to be inserted into list @c l
|
||||
@pre #include <mrw/list.hpp>
|
||||
*/
|
||||
template <typename T, typename A>
|
||||
std::list<T, A>& operator<<(std::list<T, A>& l, const T& o) throw(std::bad_exception) {
|
||||
l.push_back(o);
|
||||
return l;
|
||||
}
|
||||
|
||||
/** @brief extract the first value of a list
|
||||
|
||||
@code
|
||||
std::list<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
test>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 test=={5, 6, 7, 8}
|
||||
@endcode
|
||||
|
||||
@param l a list of values
|
||||
@param o a value to be extracted from list @c l
|
||||
@throw mrw::length_error, if list was empty on entry
|
||||
@note when something is extracted from a list, it is removed
|
||||
from the list, that means after every shift the list is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
template <typename T, typename A>
|
||||
std::list<T, A>& operator>>(std::list<T, A>& l, T& o) throw(std::exception) {
|
||||
typename std::list<T, A>::iterator it(l.begin());
|
||||
if (it==l.end())
|
||||
throw mrw::length_error(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::list<>& operator>>(std::list<>&, T&),"
|
||||
" list is empty");
|
||||
o = *it;
|
||||
l.erase(it);
|
||||
return l;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
86
mrw/map.hpp
Normal file
86
mrw/map.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__MAP__HPP__
|
||||
#define __MRW__MAP__HPP__
|
||||
|
||||
#include <map>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/string.hpp>
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief insert a value in a map
|
||||
|
||||
@code
|
||||
std::map<int, std::string> test;
|
||||
test<<std::make_pair(1, std::string("one"))
|
||||
<<std::make_pair(2, std::string("two"));
|
||||
@endcode
|
||||
|
||||
@throw mrw::invalid_argument, if element is already in map
|
||||
@param l a map of values
|
||||
@param o a value to be inserted into map @c l
|
||||
@pre #include <mrw/map.hpp>
|
||||
*/
|
||||
template <typename K, typename T, class C, typename A>
|
||||
std::map<K, T, C, A>& operator<<(std::map<K, T, C, A>& l, const std::pair<K, T>& o)
|
||||
throw(std::exception) {
|
||||
if (!l.insert(o).second)
|
||||
throw mrw::invalid_argument(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::map<>&"
|
||||
" operator<<(std::map<>&, const T&),"
|
||||
"map element already exists");
|
||||
return l;
|
||||
}
|
||||
|
||||
/** @brief extract the first value of a map
|
||||
|
||||
@code
|
||||
std::map<int, std::string> test;
|
||||
test<<std::make_pair(1, std::string("one"))
|
||||
<<std::make_pair(2, std::string("two"));
|
||||
std::pair<int, std::string> i1, i2;
|
||||
test>>i1>>i2;
|
||||
// now: i1==(1, "one") i2==(2, "two") test=={}
|
||||
@endcode
|
||||
|
||||
@param l a map of values
|
||||
@param o a value to be extracted from map @c l
|
||||
@throw mrw::length_error, if map was empty on entry
|
||||
@note when something is extracted from a map, it is removed
|
||||
from the map, that means after every shift the map is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
template <typename K, typename T, class C, typename A>
|
||||
std::map<K, T, C, A>& operator>>(std::map<K, T, C, A>& l, std::pair<K, T>& o)
|
||||
throw(std::exception) {
|
||||
typename std::map<K, T, C, A>::iterator it(l.begin());
|
||||
if (it==l.end())
|
||||
throw mrw::length_error(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::map<>& operator>>(std::map<>&, T&),"
|
||||
" map is empty");
|
||||
o = *it;
|
||||
l.erase(it);
|
||||
return l;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
82
mrw/multimap.hpp
Normal file
82
mrw/multimap.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__MULTIMAP__HPP__
|
||||
#define __MRW__MULTIMAP__HPP__
|
||||
|
||||
#include <map>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/string.hpp>
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief insert a value in a multimap
|
||||
|
||||
@code
|
||||
std::multimap<int, std::string> test;
|
||||
test<<std::make_pair(1, std::string("one"))
|
||||
<<std::make_pair(2, std::string("two"));
|
||||
@endcode
|
||||
|
||||
@throw mrw::invalid_argument, if element is already in multimap
|
||||
@param l a multimap of values
|
||||
@param o a value to be inserted into multimap @c l
|
||||
@pre #include <mrw/multimap.hpp>
|
||||
*/
|
||||
template <typename K, typename T, class C, typename A>
|
||||
std::multimap<K, T, C, A>& operator<<(std::multimap<K, T, C, A>& l, const std::pair<K, T>& o)
|
||||
throw(std::bad_exception) {
|
||||
l.insert(o);
|
||||
return l;
|
||||
}
|
||||
|
||||
/** @brief extract the first value of a multimap
|
||||
|
||||
@code
|
||||
std::multimap<int, std::string> test;
|
||||
test<<std::make_pair(1, std::string("one"))
|
||||
<<std::make_pair(2, std::string("two"));
|
||||
std::pair<int, std::string> i1, i2;
|
||||
test>>i1>>i2;
|
||||
// now: i1==(1, "one") i2==(2, "two") test=={}
|
||||
@endcode
|
||||
|
||||
@param l a multimap of values
|
||||
@param o a value to be extracted from multimap @c l
|
||||
@throw mrw::length_error, if multimap was empty on entry
|
||||
@note when something is extracted from a multimap, it is removed
|
||||
from the multimap, that means after every shift the multimap is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
template <typename K, typename T, class C, typename A>
|
||||
std::multimap<K, T, C, A>& operator>>(std::multimap<K, T, C, A>& l, std::pair<K, T>& o)
|
||||
throw(std::exception) {
|
||||
typename std::multimap<K, T, C, A>::iterator it(l.begin());
|
||||
if (it==l.end())
|
||||
throw mrw::length_error(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::multimap<>& operator>>(std::multimap<>&, T&),"
|
||||
" multimap is empty");
|
||||
o = *it;
|
||||
l.erase(it);
|
||||
return l;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
77
mrw/multiset.hpp
Normal file
77
mrw/multiset.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__MULTISET__HPP__
|
||||
#define __MRW__MULTISET__HPP__
|
||||
|
||||
#include <set>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/string.hpp>
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief insert a value in a multiset
|
||||
|
||||
@code
|
||||
std::multiset<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
@endcode
|
||||
|
||||
@param l a multiset of values
|
||||
@param o a value to be inserted into multiset @c l
|
||||
@pre #include <mrw/multiset.hpp>
|
||||
*/
|
||||
template <typename T, class C, typename A>
|
||||
std::multiset<T, C, A>& operator<<(std::multiset<T, C, A>& l, const T& o) throw(std::bad_exception) {
|
||||
l.insert(o);
|
||||
return l;
|
||||
}
|
||||
|
||||
/** @brief extract the first value of a multiset
|
||||
|
||||
@code
|
||||
std::multiset<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
test>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 test=={5, 6, 7, 8}
|
||||
@endcode
|
||||
|
||||
@param l a multiset of values
|
||||
@param o a value to be extracted from multiset @c l
|
||||
@throw mrw::length_error, if set was empty on entry
|
||||
@note when something is extracted from a multiset, it is removed
|
||||
from the multiset, that means after every shift the multiset is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
template <typename T, class C, typename A>
|
||||
std::multiset<T, C, A>& operator>>(std::multiset<T, C, A>& l, T& o) throw(std::exception) {
|
||||
typename std::multiset<T, C, A>::iterator it(l.begin());
|
||||
if (it==l.end())
|
||||
throw mrw::length_error(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::multiset<>& operator>>(std::multiset<>&, T&),"
|
||||
" multiset is empty");
|
||||
o = *it;
|
||||
l.erase(it);
|
||||
return l;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
84
mrw/set.hpp
Normal file
84
mrw/set.hpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__SET__HPP__
|
||||
#define __MRW__SET__HPP__
|
||||
|
||||
#include <set>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/string.hpp>
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief insert a value in a set
|
||||
|
||||
@code
|
||||
std::set<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
@endcode
|
||||
|
||||
@throw mrw::invalid_argument, if element is already in set
|
||||
@param l a set of values
|
||||
@param o a value to be inserted into set @c l
|
||||
@pre #include <mrw/set.hpp>
|
||||
*/
|
||||
template <typename T, class C, typename A>
|
||||
std::set<T, C, A>& operator<<(std::set<T, C, A>& l, const T& o)
|
||||
throw(std::exception) {
|
||||
if (!l.insert(o).second)
|
||||
throw mrw::invalid_argument(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::set<>&"
|
||||
" operator<<(std::set<>&, const T&),"
|
||||
"set element already exists");
|
||||
return l;
|
||||
}
|
||||
|
||||
/** @brief extract the first value of a set
|
||||
|
||||
@code
|
||||
std::set<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
test>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 test=={5, 6, 7, 8}
|
||||
@endcode
|
||||
|
||||
@param l a set of values
|
||||
@param o a value to be extracted from set @c l
|
||||
@throw mrw::length_error, if set was empty on entry
|
||||
@note when something is extracted from a set, it is removed
|
||||
from the set, that means after every shift the set is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
template <typename T, class C, typename A>
|
||||
std::set<T, C, A>& operator>>(std::set<T, C, A>& l, T& o)
|
||||
throw(std::exception) {
|
||||
typename std::set<T, C, A>::iterator it(l.begin());
|
||||
if (it==l.end())
|
||||
throw mrw::length_error(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::set<>& operator>>(std::set<>&, T&),"
|
||||
" set is empty");
|
||||
o = *it;
|
||||
l.erase(it);
|
||||
return l;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
257
mrw/stdext_test.cpp
Normal file
257
mrw/stdext_test.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#include <mrw/string.hpp>
|
||||
#include <mrw/list.hpp>
|
||||
#include <mrw/vector.hpp>
|
||||
#include <mrw/deque.hpp>
|
||||
#include <mrw/set.hpp>
|
||||
#include <mrw/map.hpp>
|
||||
#include <mrw/multiset.hpp>
|
||||
#include <mrw/multimap.hpp>
|
||||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/ui/text/TestRunner.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <cppunit/extensions/TestFactoryRegistry.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class StdExtTest: public CppUnit::TestFixture {
|
||||
public:
|
||||
void StringConv() {
|
||||
std::string s("Integer=");
|
||||
int i(4);
|
||||
CPPUNIT_ASSERT(s+mrw::string(i) == "Integer=4");
|
||||
}
|
||||
void StringShift() {
|
||||
std::string s("Integer=");
|
||||
int i(4);
|
||||
CPPUNIT_ASSERT((s<<i<<" test "<<4<<(long)5<<" xx") == "Integer=4 test 45 xx");
|
||||
int i2 = 0;
|
||||
std::string s2, s3;
|
||||
s>>s2>>s3>>i2;
|
||||
CPPUNIT_ASSERT(s2=="Integer=4");
|
||||
CPPUNIT_ASSERT(s3=="test");
|
||||
CPPUNIT_ASSERT(i2==45);
|
||||
CPPUNIT_ASSERT(s==" xx");
|
||||
s2=""; s3="";
|
||||
s>>s2>>s3;
|
||||
CPPUNIT_ASSERT(s2=="xx");
|
||||
CPPUNIT_ASSERT(s3=="");
|
||||
CPPUNIT_ASSERT(s=="");
|
||||
|
||||
}
|
||||
void StringAdd() {
|
||||
std::string s;
|
||||
s=s+(signed short)1+(signed int)2+(signed long)3+(signed char)'A'+
|
||||
(unsigned short)1+(unsigned int)2+(unsigned long)3+(unsigned char)'A'+'c';
|
||||
CPPUNIT_ASSERT(s=="1236512365c");
|
||||
s=(signed short)-4+s;
|
||||
s=(signed int)5+s;
|
||||
s=(signed long)6+s;
|
||||
s=(signed char)8+s;
|
||||
s=(unsigned short)4+s;
|
||||
s=(unsigned int)5+s;
|
||||
s=(unsigned long)6+s;
|
||||
s=(unsigned char)8+s;
|
||||
s='a'+s;
|
||||
CPPUNIT_ASSERT(s=="a8654865-41236512365c");
|
||||
s+=(signed short)-4;
|
||||
s+=(signed int)5 ;
|
||||
s+=(signed long)6;
|
||||
s+=(signed char)8 ;
|
||||
s+=(unsigned short)4;
|
||||
s+=(unsigned int)5 ;
|
||||
s+=(unsigned long)6;
|
||||
s+=(unsigned char)8 ;
|
||||
s+='a';
|
||||
CPPUNIT_ASSERT(s=="a8654865-41236512365c-45684568a");
|
||||
}
|
||||
void ListShift() {
|
||||
std::list<int> l;
|
||||
l<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
l>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 l=={5, 6, 7, 8}
|
||||
CPPUNIT_ASSERT(i1==1 && i2==2 && i3==3 && i4==4);
|
||||
CPPUNIT_ASSERT(l.size()==4);
|
||||
for (int i=0; i<4; (l.pop_front(), ++i)) {
|
||||
CPPUNIT_ASSERT(l.front()==i+5);
|
||||
}
|
||||
bool exc(false);
|
||||
try {
|
||||
l>>i1;
|
||||
} catch (mrw::length_error&) {
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
}
|
||||
void VectorShift() {
|
||||
std::vector<int> l;
|
||||
l<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
l>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 l=={5, 6, 7, 8}
|
||||
CPPUNIT_ASSERT(i1==1 && i2==2 && i3==3 && i4==4);
|
||||
CPPUNIT_ASSERT(l.size()==4);
|
||||
for (int i=0; i<4; (l.erase(l.begin()), ++i)) {
|
||||
CPPUNIT_ASSERT(l.front()==i+5);
|
||||
}
|
||||
bool exc(false);
|
||||
try {
|
||||
l>>i1;
|
||||
} catch (mrw::length_error&) {
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
}
|
||||
void DequeShift() {
|
||||
std::deque<int> l;
|
||||
l<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
l>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 l=={5, 6, 7, 8}
|
||||
CPPUNIT_ASSERT(i1==1 && i2==2 && i3==3 && i4==4);
|
||||
CPPUNIT_ASSERT(l.size()==4);
|
||||
for (int i=0; i<4; (l.erase(l.begin()), ++i)) {
|
||||
CPPUNIT_ASSERT(l.front()==i+5);
|
||||
}
|
||||
bool exc(false);
|
||||
try {
|
||||
l>>i1;
|
||||
} catch (mrw::length_error&) {
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
}
|
||||
void SetShift() {
|
||||
std::set<int> s;
|
||||
bool exc(false);
|
||||
try {
|
||||
s<<1<<2<<3<<4<<5<<6<<7<<8<<8;
|
||||
} catch (mrw::invalid_argument& e) {
|
||||
std::cout<<"******** EXCEPTION"<<std::endl
|
||||
<<e.what()<<std::endl
|
||||
<<e.stacktrace();
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
s>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 s=={5, 6, 7, 8}
|
||||
CPPUNIT_ASSERT(i1==1 && i2==2 && i3==3 && i4==4);
|
||||
CPPUNIT_ASSERT(s.size()==4);
|
||||
for (int i=0; i<4; ++i) {
|
||||
CPPUNIT_ASSERT(s.find(i+5)!=s.end());
|
||||
}
|
||||
s.erase(s.begin(), s.end());
|
||||
exc=false;
|
||||
try {
|
||||
s>>i1;
|
||||
} catch (mrw::length_error&) {
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
}
|
||||
void MapShift() {
|
||||
std::map<int, std::string> s;
|
||||
bool exc(false);
|
||||
try {
|
||||
s<<std::make_pair(1, std::string("one"))
|
||||
<<std::make_pair(2, std::string("two"))
|
||||
<<std::make_pair(2, std::string("two"));
|
||||
} catch (mrw::invalid_argument& e) {
|
||||
std::cout<<"******** EXCEPTION"<<std::endl
|
||||
<<e.what()<<std::endl
|
||||
<<e.stacktrace();
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
std::pair<int, std::string> i1, i2;
|
||||
s>>i1>>i2;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 s=={5, 6, 7, 8}
|
||||
CPPUNIT_ASSERT(i1==std::make_pair(1, std::string("one")) &&
|
||||
i2==std::make_pair(2, std::string("two")));
|
||||
CPPUNIT_ASSERT(s.size()==0);
|
||||
exc=false;
|
||||
try {
|
||||
s>>i1;
|
||||
} catch (mrw::length_error&) {
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
}
|
||||
void MultisetShift() {
|
||||
std::multiset<int> s;
|
||||
s<<1<<2<<3<<4<<5<<6<<7<<8<<9;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
s>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 s=={5, 6, 7, 8}
|
||||
CPPUNIT_ASSERT(i1==1 && i2==2 && i3==3 && i4==4);
|
||||
CPPUNIT_ASSERT(s.size()==5);
|
||||
for (int i=0; i<5; ++i) {
|
||||
CPPUNIT_ASSERT(s.find(i+5)!=s.end());
|
||||
}
|
||||
s.erase(s.begin(), s.end());
|
||||
bool exc(false);
|
||||
try {
|
||||
s>>i1;
|
||||
} catch (mrw::length_error&) {
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
}
|
||||
void MultimapShift() {
|
||||
std::multimap<int, std::string> s;
|
||||
s<<std::make_pair(1, std::string("one"))
|
||||
<<std::make_pair(2, std::string("two"))
|
||||
<<std::make_pair(2, std::string("two"));
|
||||
std::pair<int, std::string> i1, i2;
|
||||
s>>i1>>i2;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 s=={5, 6, 7, 8}
|
||||
CPPUNIT_ASSERT(i1==std::make_pair(1, std::string("one")) &&
|
||||
i2==std::make_pair(2, std::string("two")));
|
||||
CPPUNIT_ASSERT(s.size()==1);
|
||||
s>>i1;
|
||||
CPPUNIT_ASSERT(i1==std::make_pair(2, std::string("two")));
|
||||
bool exc(false);
|
||||
try {
|
||||
s>>i1;
|
||||
} catch (mrw::length_error&) {
|
||||
exc=true;
|
||||
}
|
||||
CPPUNIT_ASSERT(exc);
|
||||
}
|
||||
CPPUNIT_TEST_SUITE(StdExtTest);
|
||||
CPPUNIT_TEST(StringConv);
|
||||
CPPUNIT_TEST(StringShift);
|
||||
CPPUNIT_TEST(StringAdd);
|
||||
CPPUNIT_TEST(ListShift);
|
||||
CPPUNIT_TEST(VectorShift);
|
||||
CPPUNIT_TEST(DequeShift);
|
||||
CPPUNIT_TEST(SetShift);
|
||||
CPPUNIT_TEST(MapShift);
|
||||
CPPUNIT_TEST(MultisetShift);
|
||||
CPPUNIT_TEST(MultimapShift);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(StdExtTest);
|
||||
|
||||
int main() {
|
||||
CppUnit::TextUi::TestRunner runner;
|
||||
runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
|
||||
return runner.run() ? 0 : 1;
|
||||
}
|
450
mrw/string.hpp
Normal file
450
mrw/string.hpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__STRING__HPP__
|
||||
#define __MRW__STRING__HPP__
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace mrw {
|
||||
|
||||
/** @defgroup StdExt Extensions for C++ Standard Libraries
|
||||
|
||||
There are some feature I am often missing in standard C++. They
|
||||
are relatively easy to obtain, but they could be even simpler. I
|
||||
am mainly a convinced C++ programmer, because I love
|
||||
simplicity. This means, to convert an integer to a string,
|
||||
something like this is not simple enough:
|
||||
|
||||
@code
|
||||
int i;
|
||||
std::string s;
|
||||
[...]
|
||||
std::stringstream ss;
|
||||
ss<<"length is: "<<i<<"mm";
|
||||
ss>>s;
|
||||
@endcode
|
||||
|
||||
Why can't it simply be:
|
||||
|
||||
@code
|
||||
int i;
|
||||
std::string s;
|
||||
[...]
|
||||
s<<"length is: "<<i<<"mm";
|
||||
@endcode
|
||||
|
||||
Or:
|
||||
|
||||
@code
|
||||
int i;
|
||||
std::string s;
|
||||
[...]
|
||||
s += i; // convert i to string and append it
|
||||
@endcode
|
||||
|
||||
Because we are using the great and powerful C++ language, it can
|
||||
be! That's why you need this module.
|
||||
|
||||
In addition to the shift in and shift out operator for strings,
|
||||
you also get a shhift in and shift out operator for all STL
|
||||
container classes:
|
||||
|
||||
@code
|
||||
std::list<int> l;
|
||||
l<<1<<2<<<3<<4;
|
||||
int i1, i2, i3, i4;
|
||||
l>>i1>>i2>>i3>>i4;
|
||||
@endcode
|
||||
|
||||
And the possibility to add strings to integers and vice versa:
|
||||
|
||||
@code
|
||||
std::string s("x");
|
||||
s = 1+s+2; // s=="1x2"
|
||||
@endcode
|
||||
|
||||
@warning Please note that global namespace is polluted with some
|
||||
operators. If you don't want this, just don't include any of
|
||||
these include files files. There's no impact from this module,
|
||||
if you don't include a header, since all code is inline.
|
||||
|
||||
@warning This code is still experimental and subject to frequent
|
||||
changes! Do not rely your projects on it yet!
|
||||
|
||||
@since 1.0.0
|
||||
*/
|
||||
//@{
|
||||
|
||||
|
||||
/** @brief convert any value to a std::string
|
||||
|
||||
@code
|
||||
std::string s = mrw::string(15);
|
||||
@endcode
|
||||
|
||||
@param o a value to be converted to std::string
|
||||
@pre #include <mrw/string.hpp>
|
||||
@pre T must support operator<< to a stream
|
||||
|
||||
*/
|
||||
template <typename T> std::string string(const T& o) throw(std::bad_exception) {
|
||||
std::stringstream ss;
|
||||
ss<<o;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/** @brief convert std::string to any value
|
||||
|
||||
@code
|
||||
int i = mrw::to<int>("15");
|
||||
@endcode
|
||||
|
||||
@param s the string where a value of type @c T is extracted from
|
||||
@pre #include <mrw/string.hpp>
|
||||
@pre T must support operator>> from a stream
|
||||
*/
|
||||
template <typename T> T to(const std::string& s) throw(std::bad_exception) {
|
||||
T o;
|
||||
std::stringstream ss(s);
|
||||
ss>>o;
|
||||
return o;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
}
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief append any value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s<<"length is: "<<i<<"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
@pre T must support operator<< to a stream
|
||||
*/
|
||||
template <typename T> std::string& operator<<(std::string& s, const T& o) throw(std::bad_exception) {
|
||||
return s+=mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief extract any value from a string
|
||||
|
||||
@code
|
||||
std::string s1("length: 15 mm");
|
||||
string s2, s3;
|
||||
int i(0);
|
||||
s1>>s2>>is3;
|
||||
// now: s1=="" s2=="length:" i==15 s3=="mm"
|
||||
@endcode
|
||||
|
||||
@param s the string, from which o is extracted
|
||||
@param o the value to extract from s
|
||||
@note when something is extracted from a string, it is removed
|
||||
from the string, that means after every shift the string is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
@pre T must support operator>> from a stream
|
||||
*/
|
||||
template <typename T> std::string& operator>>(std::string& s, T& o) throw(std::bad_exception) {
|
||||
std::stringstream ss(s);
|
||||
ss>>o;
|
||||
return (s=ss.tellg()>0?s.substr(ss.tellg()):"");
|
||||
}
|
||||
|
||||
/** @brief add a @c unsigned short value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(const std::string& s, unsigned short o) throw(std::bad_exception) {
|
||||
return s+mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief append a string to a @c unsigned short value
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is prepended
|
||||
@param o the value to prepend in front of @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(unsigned short o, const std::string& s) throw(std::bad_exception) {
|
||||
return mrw::string(o)+s;
|
||||
}
|
||||
|
||||
/** @brief add an @c unsigned int value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(const std::string& s, unsigned int o) throw(std::bad_exception) {
|
||||
return s+mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief append a string to an @c unsigned int value
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is prepended
|
||||
@param o the value to prepend in front of @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(unsigned int o, const std::string& s) throw(std::bad_exception) {
|
||||
return mrw::string(o)+s;
|
||||
}
|
||||
|
||||
/** @brief add a @c unsigned long value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(const std::string& s, unsigned long o) throw(std::bad_exception) {
|
||||
return s+mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief append a string to a @c unsigned long value
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is prepended
|
||||
@param o the value to prepend in front of @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(unsigned long o, const std::string& s) throw(std::bad_exception) {
|
||||
return mrw::string(o)+s;
|
||||
}
|
||||
|
||||
/** @brief add a @c signed short value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(const std::string& s, signed short o) throw(std::bad_exception) {
|
||||
return s+mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief append a string to a @c signed short value
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is prepended
|
||||
@param o the value to prepend in front of @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(signed short o, const std::string& s) throw(std::bad_exception) {
|
||||
return mrw::string(o)+s;
|
||||
}
|
||||
|
||||
/** @brief add an @c signed int value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(const std::string& s, signed int o) throw(std::bad_exception) {
|
||||
return s+mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief append a string to an @c signed int value
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is prepended
|
||||
@param o the value to prepend in front of @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(signed int o, const std::string& s) throw(std::bad_exception) {
|
||||
return mrw::string(o)+s;
|
||||
}
|
||||
|
||||
/** @brief add a @c signed long value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(const std::string& s, signed long o) throw(std::bad_exception) {
|
||||
return s+mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief append a string to a @c signed long value
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s+"length is: "+i+"mm";
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is prepended
|
||||
@param o the value to prepend in front of @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string operator+(signed long o, const std::string& s) throw(std::bad_exception) {
|
||||
return mrw::string(o)+s;
|
||||
}
|
||||
|
||||
|
||||
/** @brief add a @c unsigned short value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s += o;
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string& operator+=(std::string& s, unsigned short o) throw(std::bad_exception) {
|
||||
return s+=mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief add an @c unsigned int value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s += o;
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string& operator+=(std::string& s, unsigned int o) throw(std::bad_exception) {
|
||||
return s+=mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief add a @c unsigned long value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s += o;
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string& operator+=(std::string& s, unsigned long o) throw(std::bad_exception) {
|
||||
return s+=mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief add a @c signed short value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s += o;
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string& operator+=(std::string& s, signed short o) throw(std::bad_exception) {
|
||||
return s+=mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief add an @c signed int value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s += o;
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string& operator+=(std::string& s, signed int o) throw(std::bad_exception) {
|
||||
return s+=mrw::string(o);
|
||||
}
|
||||
|
||||
/** @brief add a @c signed long value to a string
|
||||
|
||||
@code
|
||||
std::string s;
|
||||
s += o;
|
||||
@endcode
|
||||
|
||||
@param s the string, where @c o is appended
|
||||
@param o the value to append to @c s
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
std::string& operator+=(std::string& s, signed long o) throw(std::bad_exception) {
|
||||
return s+=mrw::string(o);
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
77
mrw/vector.hpp
Normal file
77
mrw/vector.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/** @file
|
||||
|
||||
$Id$
|
||||
|
||||
$Date$
|
||||
$Author$
|
||||
|
||||
@copy © Marc Wäckerlin
|
||||
@license LGPL, see file <a href="license.html">COPYING</a>
|
||||
|
||||
$Log$
|
||||
Revision 1.1 2004/10/07 09:31:30 marc
|
||||
new feature
|
||||
|
||||
|
||||
*/
|
||||
#ifndef __MRW__VECTOR__HPP__
|
||||
#define __MRW__VECTOR__HPP__
|
||||
|
||||
#include <vector>
|
||||
#include <mrw/exception.hpp>
|
||||
#include <mrw/string.hpp>
|
||||
|
||||
/** @addtogroup StdExt
|
||||
*/
|
||||
//@{
|
||||
|
||||
/** @brief push a value to a vector
|
||||
|
||||
@code
|
||||
std::vector<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
@endcode
|
||||
|
||||
@param l a vector of values
|
||||
@param o a value to be inserted into vector @c l
|
||||
@pre #include <mrw/vector.hpp>
|
||||
*/
|
||||
template <typename T, typename A>
|
||||
std::vector<T, A>& operator<<(std::vector<T, A>& l, const T& o) throw(std::bad_exception) {
|
||||
l.push_back(o);
|
||||
return l;
|
||||
}
|
||||
|
||||
/** @brief extract the first value of a vector
|
||||
|
||||
@code
|
||||
std::vector<int> test;
|
||||
test<<1<<2<<3<<4<<5<<6<<7<<8;
|
||||
int i1(0), i2(0), i3(0), i4(0);
|
||||
test>>i1>>i2>>i3>>i4;
|
||||
// now: i1==1 i2==2 i3==3 i4==4 test=={5, 6, 7, 8}
|
||||
@endcode
|
||||
|
||||
@param l a vector of values
|
||||
@param o a value to be extracted from vector @c l
|
||||
@throw mrw::length_error, if vector was empty on entry
|
||||
@note when something is extracted from a vector, it is removed
|
||||
from the vector, that means after every shift the vector is
|
||||
shortened by the shifted element
|
||||
@pre #include <mrw/string.hpp>
|
||||
*/
|
||||
template <typename T, typename A>
|
||||
std::vector<T, A>& operator>>(std::vector<T, A>& l, T& o) throw(std::exception) {
|
||||
typename std::vector<T, A>::iterator it(l.begin());
|
||||
if (it==l.end())
|
||||
throw mrw::length_error(std::string(__FILE__ ":")+__LINE__+
|
||||
": std::vector<>& operator>>(std::vector<>&, T&),"
|
||||
" vector is empty");
|
||||
o = *it;
|
||||
l.erase(it);
|
||||
return l;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user