- Notifies the pre_answer

- Dialplan examples
- New version of commons
- Priority of contexts
- New K3L install script - getk3l.sh
- CLI return in XML
- More options in GET and SET commands
- Events (ESL) to send/receive SMS.
- fax-adjustment-timeout implemented
- accountcode and language
- Owner destroy
- Destroy wait for resources release
- static const expression
- Hangup cause
上级 6b52a433
......@@ -62,6 +62,7 @@ endpoints/mod_loopback
#endpoints/mod_skinny
#endpoints/mod_skypopen
#endpoints/mod_h323
#endpoints/mod_khomp
#../../libs/openzap/mod_openzap
#../../libs/freetdm/mod_freetdm
#asr_tts/mod_unimrcp
......@@ -105,5 +106,4 @@ say/mod_say_ru
#say/mod_say_th
## Experimental Modules (don't cry if they're broken)
#endpoints/mod_khomp
#../../contrib/mod/xml_int/mod_xml_odbc
......@@ -38,6 +38,7 @@
<!-- <load module="mod_openzap"/> -->
<!-- <load module="mod_unicall"/> -->
<!-- <load module="mod_skinny"/> -->
<!-- <load module="mod_khomp"/> -->
<!-- Applications -->
<load module="mod_commands"/>
......
......@@ -95,16 +95,6 @@ should be opened for the channel. Limited to 25ms min, 500ms max.
<param name="r2-preconnect-wait" value="250"/>
-->
<!--
Enable/disable native bridge mode (known in pt_BR as "trombone") for calls
in the Khomp channel, passing the audio inside the board when both channels
(incoming and outgoing) are of type Khomp. This reduces the echo and the
audio delay, and frees the host from most audio processing.
(default = yes)
<param name="native-bridge" value="yes"/>
-->
<!--
Defines the incoming context for calls on E1 channels. Some wildcards are
accepted, and described in the bottom of this file.
......@@ -239,48 +229,30 @@ is enabled on the board configuration. Possible values:
<param name="input-volume" value="0"/>
-->
<!--
Sets the default AMA flags, affecting the categorization of entries in
the call detail records.
(default = default)
<param name="amaflags" value="default"/>
-->
<!--
Sets the account code for calls placed on the channel. The account code may
be any alphanumeric string
(default = KhompBoard)
default = <empty>)
<param name="accountcode" value="KhompBoard"/>
<param name="accountcode" value=""/>
-->
<!--
Set the language of the channel (useful for selecting audio messages of a
specific language on answer).
(default = <empty>)
<param name="language" value="pt_BR"/>
-->
<!--
Set the music on hold class of the channel (useful for selecting a group of
songs to play on hold).
(default = default)
<param name="mohclass" value="default"/>
-->
<!--
Sets the global orig (CALLERID) base for FXS boards. This number is added
to a sequencial number, which is incremented for each FXS board and FXS
channel in the system.
For more example of how to use this option, see channel README file,
section 'Opcoes do application Bridge', item '<action application="bridge" data="Khomp/r304" />'.
(default = 0)
<param name="fxs-global-orig" value="0200"/>
<param name="fxs-global-orig" value="0"/>
-->
<!--
......@@ -300,14 +272,6 @@ a FXS branch.
<param name="fxs-bina" value="yes"/>
-->
<!--
Enable/disable using CTbus for Khomp CTI boards in native bridge.
(WARNING: just used for internal testings!)
(default = no)
<param name="has-ctbus" value="no"/>
-->
<!--
This is the delay time to really disconnect a channel after the disconnect
event arrive. If a connect event comes up in this interval, then the
......@@ -337,18 +301,6 @@ not have any tone. Values are in milliseconds.
<param name="delay-ringback-pbx" value="2500"/>
-->
<!--
Defines if the channel should optimize audio delay by droping longstanding
packets from audio buffer. This guarantees the minimal audio delay for the
user, and avoid delays associated with miscoded SIP clients. However,
depending on the system's scheduling policy (some 2.6 kernel releases),
this may result on excessive drop of packets, and audible audio skipping.
This should not be changed naively.
(default = no)
<param name="optimize-audio-path" value="no"/>
-->
<!--
Defines if the channel should ignore some uncommon DTMF digits detected by
the board (A, B, C and D). This reduces the number of false positives which
......@@ -526,9 +478,8 @@ In the example above, the branch numbered 804 will have specific
configuration for default output volume set to +2.
Possible values to options is:
context, input-volume, output-volume language,
mohclass, amaflags, accountcode, calleridnum,
calleridname, mailbox, flash-to-digits.
context, input-volume, output-volume, language,
accountcode, calleridnum, calleridname, flash-to-digits.
-->
</fxs-options>
......
MODNAME := mod_khomp
VERBOSE := 1
#FreeSWITCH source PATH is needed:
# Set FREESWITCH_PATH
ifeq ($(strip $(FREESWITCH_PATH)),)
BASE := ../../../../
else
......@@ -11,12 +14,12 @@ curr_dir := $(shell pwd)
versions := -DFS_VERSION_MAJOR=$(shell bash $(curr_dir)/tools/getversion.sh "SWITCH_VERSION_MAJOR" $(BASE)) -DFS_VERSION_MINOR=$(shell bash $(curr_dir)/tools/getversion.sh "SWITCH_VERSION_MINOR" $(BASE)) -DFS_VERSION_MICRO=$(shell bash $(curr_dir)/tools/getversion.sh "SWITCH_VERSION_MICRO" $(BASE))
LOCAL_CFLAGS = -I./ -I./include -I./commons -I./support -D_REENTRANT -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DK3L_HOSTSYSTEM -DCOMMONS_LIBRARY_USING_FREESWITCH -g -ggdb #-DDEBUG_FLAGS
LOCAL_CFLAGS = -I./ -I./include -I./commons -I./commons/base -I./support -D_REENTRANT -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -DK3L_HOSTSYSTEM -DCOMMONS_LIBRARY_USING_FREESWITCH -g -ggdb #-DDEBUG_FLAGS
LOCAL_CFLAGS += $(versions)
LOCAL_LDFLAGS = -lk3l
LOCAL_OBJS = ./commons/k3lapi.o ./commons/k3lutil.o ./commons/config_options.o ./commons/format.o ./commons/strings.o ./commons/ringbuffer.o ./commons/verbose.o ./commons/saved_condition.o ./commons/regex.o ./commons/timer.o ./commons/configurator/configfile.o ./commons/configurator/option.o ./commons/configurator/section.o ./commons/configurator/restriction.o
LOCAL_OBJS = ./commons/base/k3lapi.o ./commons/base/k3lutil.o ./commons/base/config_options.o ./commons/base/format.o ./commons/base/strings.o ./commons/base/ringbuffer.o ./commons/base/verbose.o ./commons/base/saved_condition.o ./commons/base/regex.o ./commons/base/timer.o ./commons/base/configurator/configfile.o ./commons/base/configurator/option.o ./commons/base/configurator/section.o ./commons/base/configurator/restriction.o ./commons/base/verbose_traits.o
LOCAL_OBJS += ./support/klog-config.o ./support/klog-options.o ./support/config_defaults.o
LOCAL_OBJS += ./src/globals.o ./src/opt.o ./src/frame.o ./src/utils.o ./src/lock.o ./src/spec.o ./src/applications.o ./src/khomp_pvt_fxo.o ./src/khomp_pvt_gsm.o ./src/khomp_pvt_kxe1.o ./src/khomp_pvt_passive.o ./src/khomp_pvt.o ./src/logger.o ./src/cli.o
......@@ -27,7 +30,20 @@ conf_file_install = $(sysconfdir)/autoload_configs
include $(BASE)/build/modmake.rules
local_depend:
@if test ! -f $(curr_dir)/commons/base/verbose_traits.hpp || test ! -f $(curr_dir)/commons/base/verbose_traits.cpp ; then \
echo "Generating verbose_traits" ;\
bash $(curr_dir)/commons/tools/generate-verbose-headers.sh commons/base/ include/k3l.h ;\
fi;
depend_install:
@if test "w`kserver --version 2>/dev/null | grep 2.1`" == "w" ; then \
echo "###############################################################################" ;\
echo "Install k3l from KHOMP." ;\
echo "Run: $(curr_dir)/tools/getk3l.sh" ;\
echo "###############################################################################" ;\
exit 1;\
fi;
@echo "Copy $(conf_file_name)"
@if test -d $(conf_file_install) ; then \
if test -f $(conf_file_dir)/$(conf_file_name) ; then \
......
......@@ -64,17 +64,31 @@ namespace Atomic
PunnedType pval; pval.valtype = VAL; \
unsigned long long vexp = *(pexp.podtype); \
unsigned long long vval = *(pval.podtype); \
unsigned long long res = (unsigned long long)exp; \
unsigned long vval32 = (unsigned long)vval; \
unsigned char chg = 0; \
asm volatile("lock; cmpxchg8b %2; sete %1;" \
asm volatile( \
"xchgl %%ebx, %4;" \
"lock; cmpxchg8b %2; sete %1;" \
"movl %4, %%ebx; " \
: "+A" (vexp), /* 0 (result) */ \
"=q" (chg) /* 1 */ \
"=c" (chg) /* 1 */ \
: "m" (*(unsigned char**)(PTR)), /* 2 */ \
"b" ((unsigned long)(vval)), \
"c" ((unsigned long)(vval >> 32))); \
"c" ((unsigned long)(vval >> 32)), \
"m" (vval32)); \
*(pexp.podtype) = vexp; \
return (chg != 0 ? true : false);
// "movl %%ecx, %4;"
//
// "m" (*((unsigned long*)(*(pval.podtype)))),
// "m" ((unsigned long)(vval >> 32))
//
// "m" (*((unsigned long*)(&vval))),
// "m" ((unsigned long)(vval >> 32))
//
// unsigned long long vval = *(pval.podtype);
// unsigned long long res = (unsigned long long)exp;
//
// Types used for making CMPXCHG instructions independent from base type.
template < typename ValType, typename PodType >
......
......@@ -63,7 +63,12 @@
#error Unknown implementation selected. Please define COMMONS_LIBRARY_USING_* correctly.
#endif
#define COMMONS_INCLUDE(file) <COMMONS_IMPLEMENTATION/file>
#define COMMONS_INCLUDE(file) <system/COMMONS_IMPLEMENTATION/file>
#endif /* _CONFIG_COMMONS_HPP_ */
#define COMMONS_VERSION_MAJOR 1
#define COMMONS_VERSION_MINOR 1
#define COMMONS_AT_LEAST(x,y) \
(COMMONS_VERSION_MAJOR > x || (COMMONS_VERSION_MAJOR == x && COMMONS_VERSION_MINOR >= y))
#endif /* _CONFIG_COMMONS_HPP_ */
/*
KHOMP generic endpoint/channel library.
Copyright (C) 2007-2009 Khomp Ind. & Com.
The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Alternatively, the contents of this file may be used under the terms of the
"GNU Lesser General Public License 2.1" license (the “LGPL" License), in which
case the provisions of "LGPL License" are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms of
the LGPL License and not to allow others to use your version of this file under
the MPL, indicate your decision by deleting the provisions above and replace them
with the notice and other provisions required by the LGPL License. If you do not
delete the provisions above, a recipient may use your version of this file under
either the MPL or the LGPL License.
The LGPL header follows below:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <configurator/configfile.hpp>
#if _MSC_VER >= 1400
#undef close
#endif
void Configfile::ignore(const std::string & str)
{
_ignores.insert(str);
};
bool Configfile::select(Section **ptr, const std::string & str)
{
/* default section == this! */
*ptr = this;
/* always success by default */
return true;
};
bool Configfile::adjust(Section * section, const std::string & opt, const std::string & val)
{
return section->load(opt, val);
};
bool Configfile::deserialize(std::ifstream & fd)
{
Section * section = NULL;
/* default selection! */
if (!select(&section))
{
_errors.push_back("default selection has failed!");
return false;
}
size_t count = 0;
while (fd.good())
{
std::string str;
/* read one line! */
std::getline(fd, str);
size_t lst = str.size() - 1;
if (str.size() >= 1 && str[lst] == '\r') //cuida das quebras de linha do tipo \r\n
{
str.erase(lst,1);
--lst;
}
/* empty line! */
if (str.size() == 0)
continue;
/* comment! */
if (str[0] == '#')
continue;
++count;
if (str[0] == '[' && str[lst] == ']')
{
str.erase(0,1); --lst;
str.erase(lst,1); --lst;
if (!select(&section, str))
{
_errors.push_back(STG(FMT("erroneous section '%s'") % str));
/* ignore this section */
section = NULL;
continue;
}
}
else
{
std::string::size_type pos = str.find('=');
if (pos == std::string::npos)
{
_errors.push_back(STG(FMT("erroneous separator '%s'") % str));
continue;
};
if (section == NULL)
{
_errors.push_back(STG(FMT("no section for option '%s'") % str));
continue;
}
std::string opt(str.substr(0,pos));
std::string val(str.substr(pos+1));
if (_ignores.find(opt) != _ignores.end())
continue;
if (val == "@") val = "";
if (adjust(section, opt, val))
continue;
_errors.push_back(STG(FMT("option '%s' does "
"not exist or '%s' is not a valid value (at section '%s')")
% opt % val % section->name()));
}
}
// retorna 'true' se arquivo tinha alguma coisa valida.
return (count != 0);
}
bool Configfile::obtain()
{
std::ifstream fd(_filename.c_str());
if (!fd.is_open())
{
_errors.push_back(STG(FMT("unable to open file '%s': %s")
% _filename % strerror(errno)));
return false;
};
if (!deserialize(fd))
{
fd.close();
return false;
}
fd.close();
return true;
};
void Configfile::recurse(std::ofstream & fd, Section * section)
{
typedef Section::SectionMap::const_iterator SectionIter;
typedef Section::OptionMap::const_iterator OptionIter;
for (OptionIter i = section->option_begin(); i != section->option_end(); i++)
{
std::string res;
if ((*i).second.store(res))
{
if (res == "") res = "@";
fd << (*i).first << "=" << res << std::endl;
}
}
if (!section->recursive())
return;
for (SectionIter j = section->section_begin(); j != section->section_end(); j++)
recurse(fd, (*j).second);
}
bool Configfile::serialize(std::ofstream & fd)
{
recurse(fd, this);
return true;
}
bool Configfile::provide()
{
std::string tmp(_filename);
tmp += ".new";
std::ofstream fd(tmp.c_str());
if (!fd.good())
{
_errors.push_back(STG(FMT("unable to open file '%s': %s")
% tmp % strerror(errno)));
return false;
}
if (!serialize(fd))
{
fd.close();
return false;
}
fd.close();
if (rename(tmp.c_str(), _filename.c_str()) != 0)
{
_errors.push_back(STG(FMT("unable to replace config file '%s': %s")
% _filename % strerror(errno)));
return false;
}
return true;
}
#if _MSC_VER >= 1400
#define close _close
#endif
/*
/*
KHOMP generic endpoint/channel library.
Copyright (C) 2007-2009 Khomp Ind. & Com.
Copyright (C) 2007-2009 Khomp Ind. & Com.
The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
......@@ -23,20 +23,20 @@
The LGPL header follows below:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <vector>
......@@ -53,38 +53,38 @@
struct Configfile: public Section
{
typedef std::list < std::string > ErrorVector;
typedef std::set < std::string > NameSet;
typedef std::list < std::string > ErrorVector;
typedef std::set < std::string > NameSet;
Configfile(std::string name, std::string desc)
: Section(name, desc), _good(false) {};
Configfile(const std::string & name, const std::string & desc)
: Section(name, desc), _good(false) {};
virtual ~Configfile() {};
virtual ~Configfile() {};
bool good() { return _good; };
std::string & filename() { return _filename; };
bool good() const { return _good; };
const std::string & filename() const { return _filename; };
ErrorVector & errors() { return _errors; };
const ErrorVector & errors() const { return _errors; };
void ignore(std::string);
void ignore(const std::string &);
virtual bool obtain();
virtual bool provide();
virtual bool obtain();
virtual bool provide();
protected:
virtual bool select(Section **, std::string str = "");
virtual bool adjust(Section *, std::string & opt, std::string & val);
virtual bool select(Section **, const std::string & str = "");
virtual bool adjust(Section *, const std::string & opt, const std::string & val);
virtual bool deserialize(std::ifstream &);
virtual bool serialize(std::ofstream &);
virtual bool deserialize(std::ifstream &);
virtual bool serialize(std::ofstream &);
void recurse(std::ofstream &, Section *);
void recurse(std::ofstream &, Section *);
protected:
bool _good;
ErrorVector _errors;
NameSet _ignores;
std::string _filename;
bool _good;
ErrorVector _errors;
NameSet _ignores;
std::string _filename;
};
#endif /* _CONFIG_CONFIGFILE_HPP_ */
......@@ -39,89 +39,148 @@
*/
#include <refcounter.hpp>
#include <configurator/option.hpp>
#include <typeinfo>
bool Option::equals(const std::string & value) const
{
switch (_restriction.numeral())
{
case Restriction::N_UNIQUE:
{
Restriction::Value my_value;
#ifndef _VARIANT_H_
#define _VARIANT_H_
if (!_restriction.get(Restriction::F_USER, my_value))
return false;
/* this is internal, should not be used by the user */
struct NoArgumentDefined {};
return (my_value == value);
}
case Restriction::N_MULTIPLE:
{
Restriction::Vector my_values;
template < typename UserReturnType, typename UserArgumentType = NoArgumentDefined >
struct VariantBaseType
{
typedef UserReturnType ReturnType;
typedef UserArgumentType ArgumentType;
if (!_restriction.get(Restriction::F_USER, my_values))
return false;
virtual ~VariantBaseType() {};
for (Restriction::Vector::iterator i = my_values.begin(); i != my_values.end(); i++)
{
if ((*i) == value)
return true;
}
virtual int which() = 0;
return false;
}
}
virtual ReturnType visit(void) { return ReturnType(); };
virtual ReturnType visit(ArgumentType) { return ReturnType(); };
};
return false;
}
template < typename BaseType = VariantBaseType < void > >
struct Variant: NEW_REFCOUNTER(Variant < BaseType >)
bool Option::load(const std::string & value)
{
typedef typename BaseType::ReturnType ReturnType;
typedef typename BaseType::ArgumentType ArgumentType;
bool ret = _restriction.set( (const Restriction::Format)Restriction::F_FILE, value);
struct InvalidType {};
if (ret) _modified = false;
Variant(BaseType * value, bool is_owner = false)
: _value(value), _is_owner(is_owner) {};
return ret;
}
Variant(const Variant & v)
: INC_REFCOUNTER(v, Variant < BaseType >),
_value(v._value), _is_owner(v._is_owner) {};
bool Option::change(const std::string & value)
{
bool ret = _restriction.set(Restriction::F_FILE, value);
virtual ~Variant() {};
if (ret) _modified = true;
void unreference()
{
if (_is_owner && _value)
{
delete _value;
_value = 0;
}
};
return ret;
}
template < typename ValueType >
ValueType & get(void)
bool Option::store(std::string & value) const
{
switch (_restriction.numeral())
{
try
{
ValueType & ret = dynamic_cast < ValueType & > (*_value);
return ret;
}
catch (std::bad_cast & e)
case Restriction::N_UNIQUE:
return _restriction.get(Restriction::F_FILE, value);
case Restriction::N_MULTIPLE:
{
throw InvalidType();
Restriction::Vector values;
if (!_restriction.get(Restriction::F_FILE, values))
return false;
Strings::Merger strs;
for (Restriction::Vector::iterator i = values.begin(); i != values.end(); i++)
strs.add(*i);
value = strs.merge(",");
return true;
}
};
int which()
{
return _value->which();
default:
return false;
}
}
/*
Option::Flags Option::set(const char * value)
{
std::string str_value(value);
return set(str_value);
}
*/
Option::Flags Option::set(const Restriction::Value & value)
{
Restriction::Value last_value, curr_value;
Flags flags;
bool ret1 = _restriction.get(Restriction::F_USER, last_value);
if (!_restriction.set(Restriction::F_USER, value))
return flags;
flags[F_ADJUSTED] = true;
ReturnType visit(void)
bool ret2 = _restriction.get(Restriction::F_USER, curr_value);
if (!ret1 || (ret2 && (last_value != curr_value)))
{
return _value->visit();
flags[F_MODIFIED] = true;
_modified = true;
}
ReturnType visit(ArgumentType arg)
return flags;
}
Option::Flags Option::set(const Restriction::Vector & values)
{
Restriction::Vector last_values, curr_values;
Flags flags;
bool ret1 = _restriction.get(Restriction::F_USER, last_values);
if (!_restriction.set(Restriction::F_USER, values))
return flags;
flags[F_ADJUSTED] = true;
bool ret2 = _restriction.get(Restriction::F_USER, curr_values);
if (!ret1 || (ret2 && (last_values != curr_values)))
{
return _value->visit(arg);
flags[F_MODIFIED] = true;
_modified = true;
}
protected:
BaseType * _value;
bool _is_owner;
};
return flags;
}
#endif /* _VARIANT_H_ */
bool Option::get(Restriction::Value & value) const
{
return _restriction.get(Restriction::F_USER, value);
}
bool Option::get(Restriction::Vector & values) const
{
return _restriction.get(Restriction::F_USER, values);
}
/*
/*
KHOMP generic endpoint/channel library.
Copyright (C) 2007-2009 Khomp Ind. & Com.
Copyright (C) 2007-2009 Khomp Ind. & Com.
The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
......@@ -23,20 +23,20 @@
The LGPL header follows below:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <iostream>
......@@ -45,6 +45,7 @@
#include <list>
#include <vector>
#include <format.hpp>
#include <strings.hpp>
#include <configurator/restriction.hpp>
......@@ -56,67 +57,72 @@ struct Option
{
enum FlagTypes
{
F_MODIFIED = 0x0, /* if option was modified */
F_ADJUSTED = 0x1, /* if option was correctly formated */
F_MODIFIED = 0x0, /* if option was modified */
F_ADJUSTED = 0x1, /* if option was correctly formated */
};
struct Flags: public std::vector<bool>
{
Flags(): std::vector<bool>(2) {};
};
struct Flags: public std::vector<bool>
{
Flags(): std::vector<bool>(2) {};
};
typedef Restriction::Value Value;
typedef Restriction::Vector Vector;
typedef Restriction::Value Value;
typedef Restriction::Vector Vector;
/* exception */
struct InvalidDefaultValue
struct InvalidDefaultValue: public std::runtime_error
{
InvalidDefaultValue(std::string name, std::string value)
: _name(name), _value(value) {};
InvalidDefaultValue(const std::string & name, const std::string & value)
: std::runtime_error(STG(FMT("invalid default value '%s' for option '%s'") % value % name)),
_name(name), _value(value)
{};
std::string & name() { return _name; };
std::string & value() { return _value; };
~InvalidDefaultValue() throw ()
{};
const std::string & name() const { return _name; };
const std::string & value() const { return _value; };
protected:
std::string _name;
std::string _value;
const std::string _name;
const std::string _value;
};
Option(std::string name, std::string desc, std::string defvalue, Restriction restriction)
: _name(name), _desc(desc), _restriction(restriction), _modified(true)
{
std::string value(defvalue);
Option(const std::string & name, const std::string & desc, const std::string & defvalue, const Restriction & restriction)
: _name(name), _description(desc), _restriction(restriction), _modified(true)
{
// std::string value(defvalue);
if (!(set(value)[F_ADJUSTED]))
if (!(set(defvalue)[F_ADJUSTED]))
throw InvalidDefaultValue(name, defvalue);
}
}
const std::string & name() { return _name; };
const std::string & description() { return _desc; };
const std::string & name() const { return _name; };
const std::string & description() const { return _description; };
Restriction & restriction() { return _restriction; };
bool modified() { return _modified; };
const Restriction & restriction() const { return _restriction; };
bool modified() const { return _modified; };
public:
bool load(std::string &);
bool change(std::string &);
bool store(std::string &);
bool load(const std::string &);
bool change(const std::string &);
bool store(std::string &) const;
Flags set(const char *);
Flags set(Value &);
Flags set(Vector &);
// Flags set(const char *);
Flags set(const Value &);
Flags set(const Vector &);
bool get(Value &);
bool get(Vector &);
bool get(Value &) const;
bool get(Vector &) const;
bool equals(std::string &);
bool equals(const std::string &) const;
protected:
std::string _name;
std::string _desc;
const std::string _name;
const std::string _description;
Restriction _restriction;
bool _modified;
Restriction _restriction;
bool _modified;
};
#endif /* _CONFIG_OPTION_HPP_ */
/*
/*
KHOMP generic endpoint/channel library.
Copyright (C) 2007-2009 Khomp Ind. & Com.
Copyright (C) 2007-2009 Khomp Ind. & Com.
The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
......@@ -23,108 +23,114 @@
The LGPL header follows below:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <configurator/section.hpp>
void Section::options(Section::OptionVector & vec)
void Section::options(Section::OptionVector & vec) const
{
for (OptionMap::iterator it = _options.begin(); it != _options.end();)
{
vec.push_back(&((*it).second));
++it;
}
for (OptionMap::const_iterator it = _options.begin(); it != _options.end();)
{
vec.push_back(const_cast< Option * >(&(it->second)));
++it;
}
}
void Section::sections(Section::SectionVector & vec)
void Section::sections(Section::SectionVector & vec) const
{
for (SectionMap::iterator it = _sections.begin(); it != _sections.end();)
{
vec.push_back((*it).second);
++it;
}
for (SectionMap::const_iterator it = _sections.begin(); it != _sections.end();)
{
vec.push_back(const_cast< Section * >(it->second));
++it;
}
}
/*********/
Option * Section::option_find(std::string & str, bool recurse)
Option * Section::option_find(const std::string & str, bool recurse) const
{
OptionMap::iterator i = _options.find(str);
OptionMap::const_iterator i = _options.find(str);
if (i == _options.end())
if (i == _options.end())
{
if (!recurse)
throw not_found();
throw OptionNotFound(str, _name);
// throw not_found();
for (SectionMap::iterator i = _sections.begin(); i != _sections.end(); i++)
for (SectionMap::const_iterator i = _sections.begin(); i != _sections.end(); i++)
{
try
{
return i->second->option_find(str, recurse);
}
catch (not_found & e)
catch (NotFound & e)
{
/* keep looping! */
};
}
throw not_found();
// throw not_found();
throw OptionNotFound(str, _name);
}
return &((*i).second);
return const_cast< Option * >(&(i->second));
}
/*
Option * Section::option_find(const char * str, bool recurse)
{
std::string sstr(str);
return option_find(sstr, recurse);
std::string sstr(str);
return option_find(sstr, recurse);
}
*/
/*********/
Section * Section::section_find(std::string & str, bool recurse)
Section * Section::section_find(const std::string & str, bool recurse) const
{
SectionMap::iterator i = _sections.find(str);
SectionMap::const_iterator i = _sections.find(str);
if (i == _sections.end())
if (i == _sections.end())
{
if (!recurse)
throw not_found();
throw SectionNotFound(str, _name);
for (SectionMap::iterator i = _sections.begin(); i != _sections.end(); i++)
for (SectionMap::const_iterator i = _sections.begin(); i != _sections.end(); i++)
{
try
{
return i->second->section_find(str, recurse);
}
catch (not_found & e)
catch (NotFound & e)
{
/* keep looping! */
};
}
throw not_found();
throw SectionNotFound(str, _name);
}
return ((*i).second);
return const_cast< Section * >(i->second);
}
/*
Section * Section::section_find(const char * str, bool recurse)
{
std::string sstr(str);
return section_find(sstr, recurse);
std::string sstr(str);
return section_find(sstr, recurse);
}
*/
/*
KHOMP generic endpoint/channel library.
Copyright (C) 2007-2009 Khomp Ind. & Com.
The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Alternatively, the contents of this file may be used under the terms of the
"GNU Lesser General Public License 2.1" license (the “LGPL" License), in which
case the provisions of "LGPL License" are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms of
the LGPL License and not to allow others to use your version of this file under
the MPL, indicate your decision by deleting the provisions above and replace them
with the notice and other provisions required by the LGPL License. If you do not
delete the provisions above, a recipient may use your version of this file under
either the MPL or the LGPL License.
The LGPL header follows below:
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _CONFIG_SECTION_HPP_
#define _CONFIG_SECTION_HPP_
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
#include <format.hpp>
#include <configurator/option.hpp>
struct Section
{
typedef std::map < std::string, Option > OptionMap;
typedef std::vector< Option * > OptionVector;
typedef std::map < std::string, Section * > SectionMap;
typedef std::vector < Section * > SectionVector;
struct NotFound: public std::runtime_error
{
NotFound(const std::string & type, const std::string & name, const std::string & me)
: std::runtime_error(STG(FMT("%s '%s' not found on section '%s'") % type % name % me)) {};
};
struct OptionNotFound: public NotFound
{
OptionNotFound(const std::string & name, const std::string & me)
: NotFound("option", name, me) {};
};
struct SectionNotFound: public NotFound
{
SectionNotFound(const std::string & name, const std::string & me)
: NotFound("section", name, me) {};
};
typedef NotFound not_found; /* backward compatibility */
// protected:
Section(const std::string & name, const std::string & desc, bool recursive = true)
: _name(name), _description(desc), _recursive(recursive) {};
void add(const Option & o)
{
_options.insert(std::pair<std::string,Option>(o.name(), o));
};
void del(const std::string & name)
{
_options.erase(name);
};
void add(Section * s)
{
_sections.insert(std::pair< std::string, Section * >(s->name(), s));
};
public:
const std::string & name() const { return _name; };
const std::string & description() const { return _description; };
const bool recursive() const { return _recursive; };
OptionMap::const_iterator option_begin() const { return _options.begin(); };
OptionMap::const_iterator option_end() const { return _options.end(); };
SectionMap::const_iterator section_begin() const { return _sections.begin(); };
SectionMap::const_iterator section_end() const { return _sections.end(); };
/**/
// Option * option_find(const char *, bool recurse = false) const;
// Section * section_find(const char *, bool recurse = false) const;
Option * option_find(const std::string &, bool recurse = false) const;
Section * section_find(const std::string &, bool recurse = false) const;
/**/
void options(OptionVector &) const;
void sections(SectionVector &) const;
/**/
template < typename T, typename F >
bool search_and_apply(const std::string & key, T & value, F f)
{
OptionMap::iterator i = _options.find(key);
if (i != _options.end())
return f(i->second);
if (!_recursive)
return false;
return (find_if(_sections.begin(), _sections.end(), f) != _sections.end());
}
private:
struct ConstKeyValue
{
ConstKeyValue(const std::string & k, const std::string &v)
: _k(k), _v(v) {};
const std::string & _k;
const std::string & _v;
};
struct KeyValue
{
KeyValue(const std::string & k, std::string &v)
: _k(k), _v(v) {};
const std::string & _k;
std::string & _v;
};
struct load_section: protected ConstKeyValue
{
load_section(const std::string & k, const std::string & v): ConstKeyValue(k,v) {};
bool operator()(Option & o) { return o.load(_v); };
bool operator()(SectionMap::value_type & v) { return v.second->load(_k,_v); };
};
struct change_section: protected ConstKeyValue
{
change_section(const std::string & k, const std::string & v): ConstKeyValue(k,v) {};
bool operator()(Option & o) { return o.change(_v); };
bool operator()(SectionMap::value_type & v) { return v.second->change(_k,_v); };
};
struct store_section: protected KeyValue
{
store_section(const std::string & k, std::string & v): KeyValue(k,v) {};
bool operator()(Option & o) { return o.store(_v); };
bool operator()(SectionMap::value_type & v) { return v.second->store(_k,_v); };
};
struct set_section: protected ConstKeyValue
{
set_section(const std::string & k, const std::string & v): ConstKeyValue(k,v) {};
bool operator()(Option & o) { return (o.set(_v))[Option::F_ADJUSTED]; };
bool operator()(SectionMap::value_type & v) { return v.second->set(_k,_v); };
};
struct get_section: protected KeyValue
{
get_section(const std::string & k, std::string & v): KeyValue(k,v) {};
bool operator()(Option & o) { return o.get(_v); };
bool operator()(SectionMap::value_type & v) { return v.second->get(_k,_v); };
};
struct modified_section
{
bool operator()(const OptionMap::value_type & v) { return v.second.modified(); };
bool operator()(const SectionMap::value_type & v) { return v.second->modified(); };
};
public:
/*
bool load(const char * key, const std::string value)
{
std::string skey(key);
return search_and_apply(skey, value, load_section(skey, value));
}
*/
bool load(const std::string & key, const std::string & value)
{
return search_and_apply(key, value, load_section(key, value));
}
bool change(const std::string & key, const std::string & value)
{
return search_and_apply(key, value, change_section(key, value));
}
bool store(const std::string & key, std::string & value)
{
return search_and_apply(key, value, store_section(key, value));
}
bool set(const std::string & key, const std::string & value)
{
return search_and_apply(key, value, set_section(key, value));
}
bool get(const std::string & key, std::string & value)
{
return search_and_apply(key, value, get_section(key, value));
}
bool modified() const
{
return ((find_if(_options.begin(), _options.end(), modified_section()) != _options.end()) ||
(find_if(_sections.begin(), _sections.end(), modified_section()) != _sections.end()));
}
private:
Section(): _name(""), _description(""), _recursive(false) {};
protected:
const std::string _name;
const std::string _description;
OptionMap _options;
SectionMap _sections;
const bool _recursive;
};
#endif /* _CONFIG_SECTION_HPP_ */
#ifndef _CONST_THIS_H_
#define _CONST_THIS_H_
template < typename T >
struct ConstThis
{
T const & constThis() const
{
// TODO: will this return the right reference?
return static_cast<const T&>(*this);
}
};
#endif /* _CONST_THIS_H_ */
......@@ -42,27 +42,7 @@
#include "format.hpp"
//#include <iostream>
Format::Format(const char * format_string, bool raise_exception)
: _format(format_string), _valid(true), _raise(raise_exception)
{
initialize(format_string);
}
/*
Format::Format(std::string & format_string, bool raise_exception)
: _format(NULL), _valid(true), _raise(raise_exception)
{
initialize(format_string.c_str());
}
*/
Format::Format(std::string format_string, bool raise_exception)
: _format(format_string), _valid(true), _raise(raise_exception)
{
initialize(format_string.c_str());
}
void Format::initialize(const char * format_string)
void FormatTraits::initialize(const char * format_string)
{
std::string txt;
......@@ -82,7 +62,6 @@ void Format::initialize(const char * format_string)
if (*ptr2 == '%')
{
txt += *ptr;
ptr += 2;
continue;
}
......@@ -238,36 +217,20 @@ void Format::initialize(const char * format_string)
push_argument(txt, T_LITERAL);
}
void Format::mark_invalid(std::string & msg)
void FormatTraits::push_argument(std::string & data, FormatTraits::Type type)
{
if (_valid)
{
_valid = false;
std::string finalmsg;
finalmsg += "** INVALID FORMAT: ";
finalmsg += msg;
finalmsg += " **";
_result = finalmsg;
}
}
// std::cerr << "pushing type (" << type << ") with format (" << data << ")" << std::endl;
void Format::raise_check(void)
{
if (!_valid && _raise)
throw InvalidFormat(_result);
_args.push(Argument(data, type));
data.clear();
}
bool Format::validity_check(void)
void FormatTraits::pop_argument(void)
{
raise_check();
return _valid;
_args.pop();
}
const Format::Argument * Format::next_argument(void)
const FormatTraits::Argument * FormatTraits::next_argument(void)
{
// std::cerr << "size: " << _args.size() << std::endl;
......@@ -294,38 +257,81 @@ const Format::Argument * Format::next_argument(void)
}
}
void Format::pop_argument(void)
/******************************************************************/
#if 0
Format::Format(const char * format_string, bool raise_exception)
: _format(format_string), _valid(true), _raise(raise_exception)
{
FormatTraits::initialize(format_string);
}
Format::Format(std::string format_string, bool raise_exception)
: _format(format_string), _valid(true), _raise(raise_exception)
{
_args.pop();
FormatTraits::initialize(format_string.c_str());
}
void Format::push_argument(std::string & data, Format::Type type)
/*
Format::Format(std::string & format_string, bool raise_exception)
: _format(NULL), _valid(true), _raise(raise_exception)
{
// std::cerr << "pushing type (" << type << ") with format (" << data << ")" << std::endl;
initialize(format_string.c_str());
}
*/
_args.push(Argument(data, type));
data.clear();
void Format::mark_invalid(std::string & msg)
{
if (_valid)
{
_valid = false;
_result = "** INVALID FORMAT: ";
_result += msg;
_result += " **";
}
}
std::string Format::str()
void Format::raise(void) const
{
if (!validity_check())
return _result;
if (!_valid)
{
// call specialized class
FormatException::raise(_result);
}
}
if (next_argument() == NULL)
bool Format::valid(void) const
{
// raise();
return _valid;
}
std::string Format::str()
{
if (!valid())
return _result;
std::string msg;
// try
// {
if (next_argument() != NULL)
{
std::string msg;
msg += "too few arguments passed for format '";
msg += _format;
msg += "' (";
msg += _format;
msg += ")";
msg += "too few arguments passed for format '";
msg += _format;
msg += "' (";
msg += _format;
msg += ")";
mark_invalid(msg);
mark_invalid(msg);
return _result;
return _result;
}
// catch (NoArgumentLeft e)
// {
// return _result;
// }
}
#endif
......@@ -46,12 +46,13 @@
namespace Function
{
struct EmptyFunction {};
struct EmptyFunction {};
struct NonMemberFunction {};
/**/
template < typename FunctionTraits >
struct StorageBase: NEW_REFCOUNTER(StorageBase < FunctionTraits >)
struct StorageBase: COUNTER_SUPER(StorageBase < FunctionTraits >)
{
typedef typename FunctionTraits::BaseType BaseType;
......@@ -59,27 +60,33 @@ namespace Function
typedef typename FunctionTraits::ObjType ObjType;
template < typename Functor >
StorageBase(Functor f)
: _object(reinterpret_cast<ObjType>(new Functor(f))),
_function(reinterpret_cast<FunType>(&(Functor::operator()))),
StorageBase(const Functor f)
: _object(reinterpret_cast< ObjType >(new Functor(f))),
_function(reinterpret_cast< FunType >(&Functor::operator())),
_malloced(true)
{};
template < typename Functor >
StorageBase(Functor & f, bool malloced)
: _object(reinterpret_cast<ObjType>((malloced ? new Functor(f) : &f))),
_function(reinterpret_cast<FunType>(&(Functor::operator()))),
: _object(reinterpret_cast< ObjType >((malloced ? new Functor(f) : &f))),
_function(reinterpret_cast< FunType >(&Functor::operator())),
_malloced(malloced)
{};
StorageBase(FunType const * member)
: _object(reinterpret_cast< ObjType >(0)),
_function(reinterpret_cast< FunType >(member)),
_malloced(false)
{};
StorageBase()
: _object(reinterpret_cast<ObjType>(0)),
_function(reinterpret_cast<FunType>(0)),
: _object(reinterpret_cast< ObjType >(0)),
_function(reinterpret_cast< FunType >(0)),
_malloced(false)
{};
StorageBase(const StorageBase & o)
: INC_REFCOUNTER(o, StorageBase < FunctionTraits >),
: COUNTER_REFER(o, StorageBase < FunctionTraits >),
_object(o._object), _function(o._function), _malloced(o._malloced)
{};
......@@ -95,9 +102,9 @@ namespace Function
template < typename Functor >
void operator=(Functor f)
{
_object = reinterpret_cast<ObjType>(new Functor(f)),
_function = reinterpret_cast<FunType>(&(Functor::operator()));
_malloced = false;
_object = reinterpret_cast< ObjType >(new Functor(f)),
_function = reinterpret_cast< FunType >(&Functor::operator());
_malloced = true;
}
protected:
......@@ -229,10 +236,15 @@ namespace Function
typedef StorageBase < Function0Traits < R > > Storage;
template < typename Functor >
Function0(Functor f): Storage(f) {};
Function0(const Functor f)
: Storage(f) {};
template < typename Functor >
Function0(Functor & f, bool m): Storage(f, m) {};
Function0(Functor & f, bool m)
: Storage(f, m) {};
Function0(const typename Function0Traits < R >::FunType * m)
: Storage(m) {};
Function0() {};
......@@ -243,6 +255,18 @@ namespace Function
return ((Storage::_object)->*(Storage::_function))();
}
template < typename Object >
R operator()(Object * object)
{
if (reinterpret_cast<void *>(Storage::_function) == 0)
throw EmptyFunction();
if (reinterpret_cast<void *>(Storage::_object) != 0)
throw NonMemberFunction();
return (reinterpret_cast< typename Function0Traits < R >::ObjType *>(object)->*(Storage::_function))();
}
};
template < typename R, typename A0 >
......@@ -251,10 +275,15 @@ namespace Function
typedef StorageBase < Function1Traits < R, A0 > > Storage;
template < typename Functor >
Function1(Functor f): Storage(f) {};
Function1(const Functor f)
: Storage(f) {};
template < typename Functor >
Function1(Functor & f, bool m): Storage(f, m) {};
Function1(Functor & f, bool m)
: Storage(f, m) {};
Function1(const typename Function1Traits < R, A0 >::FunType * m)
: Storage(m) {};
Function1() {};
......@@ -265,6 +294,18 @@ namespace Function
return ((Storage::_object)->*(Storage::_function))(a0);
}
template < typename Object >
R operator()(Object * object, A0 a0)
{
if (reinterpret_cast<void *>(Storage::_function) == 0)
throw EmptyFunction();
if (reinterpret_cast<void *>(Storage::_object) != 0)
throw NonMemberFunction();
return (reinterpret_cast< typename Function1Traits < R, A0 >::ObjType *>(object)->*(Storage::_function))(a0);
}
};
template < typename R, typename A0, typename A1 >
......@@ -273,10 +314,15 @@ namespace Function
typedef StorageBase < Function2Traits < R, A0, A1 > > Storage;
template < typename Functor >
Function2(Functor f): Storage(f) {};
Function2(const Functor f)
: Storage(f) {};
template < typename Functor >
Function2(Functor & f, bool m): Storage(f, m) {};
Function2(Functor & f, bool m)
: Storage(f, m) {};
Function2(const typename Function2Traits < R, A0, A1 >::FunType * m)
: Storage(m) {};
Function2() {};
......@@ -287,6 +333,18 @@ namespace Function
return ((Storage::_object)->*(Storage::_function))(a0, a1);
}
template < typename Object >
R operator()(Object * object, A0 a0, A1 a1)
{
if (reinterpret_cast<void *>(Storage::_function) == 0)
throw EmptyFunction();
if (reinterpret_cast<void *>(Storage::_object) != 0)
throw NonMemberFunction();
return (reinterpret_cast< typename Function2Traits < R, A0, A1 >::ObjType *>(object)->*(Storage::_function))(a0, a1);
}
};
template < typename R, typename A0, typename A1, typename A2 >
......@@ -295,20 +353,37 @@ namespace Function
typedef StorageBase < Function3Traits < R, A0, A1, A2 > > Storage;
template < typename Functor >
Function3(Functor f): Storage(f) {};
Function3(const Functor f)
: Storage(f) {};
template < typename Functor >
Function3(Functor & f, bool m): Storage(f, m) {};
Function3(Functor & f, bool m)
: Storage(f, m) {};
Function3(const typename Function3Traits < R, A0, A1, A2 >::FunType * m)
: Storage(m) {};
Function3() {};
R operator()(A0 a0, A1 a1, A2 a2)
{
if (reinterpret_cast<void *>(Storage::_object) == 0)
if (reinterpret_cast<const void *>(Storage::_object) == 0)
throw EmptyFunction();
return ((Storage::_object)->*(Storage::_function))(a0, a1, a2);
}
template < typename Object >
R operator()(Object * object, A0 a0, A1 a1, A2 a2)
{
if (reinterpret_cast<void *>(Storage::_function) == 0)
throw EmptyFunction();
if (reinterpret_cast<void *>(Storage::_object) != 0)
throw NonMemberFunction();
return (reinterpret_cast< typename Function3Traits < R, A0, A1, A2 >::ObjType *>(object)->*(Storage::_function))(a0, a1, a2);
}
};
template < typename R, typename A0, typename A1, typename A2, typename A3 >
......@@ -317,10 +392,15 @@ namespace Function
typedef StorageBase < Function4Traits < R, A0, A1, A2, A3 > > Storage;
template < typename Functor >
Function4(Functor f): Storage(f) {};
Function4(const Functor f)
: Storage(f) {};
template < typename Functor >
Function4(Functor & f, bool m): Storage(f, m) {};
Function4(Functor & f, bool m)
: Storage(f, m) {};
Function4(const typename Function4Traits < R, A0, A1, A2, A3 >::FunType * m)
: Storage(m) {};
Function4() {};
......@@ -331,6 +411,18 @@ namespace Function
return ((Storage::_object)->*(Storage::_function))(a0, a1, a2, a3);
}
template < typename Object >
R operator()(Object * object, A0 a0, A1 a1, A2 a2, A3 a3)
{
if (reinterpret_cast<void *>(Storage::_function) == 0)
throw EmptyFunction();
if (reinterpret_cast<void *>(Storage::_object) != 0)
throw NonMemberFunction();
return (reinterpret_cast< typename Function4Traits < R, A0, A1, A2, A3 >::ObjType *>(object)->*(Storage::_function))(a0, a1, a2, a3);
}
};
};
......
......@@ -47,32 +47,32 @@
template < typename Type >
struct Initializer: public std::vector< Type >
{
typedef std::vector< Type > super;
typedef std::vector< Type > Super;
Initializer(Type e) { push_back(e); };
Initializer(Type & e) { push_back(e); };
Initializer(Type e) { Super::push_back(e); };
Initializer(Type & e) { Super::push_back(e); };
Initializer & operator&(Initializer v)
Initializer & operator&(const Initializer v)
{
insert(super::end(), v.begin(), v.end());
Super::insert(Super::end(), v.begin(), v.end());
return *this;
};
Initializer & operator&(Initializer & v)
{
insert(super::end(), v.begin(), v.end());
Super::insert(Super::end(), v.begin(), v.end());
return *this;
};
Initializer & operator&(Type v)
{
insert(super::end(), v);
Super::insert(Super::end(), v);
return *this;
};
Initializer & operator&(Type & v)
{
insert(super::end(), v);
Super::insert(Super::end(), v);
return *this;
};
};
......
......@@ -41,18 +41,16 @@
#include <k3lapi.hpp>
#include <format.hpp>
#include <verbose.hpp>
#include <string.h>
K3LAPI::K3LAPI(bool has_exceptions)
: _has_exceptions(has_exceptions),
_device_count(0), _channel_count(0), _link_count(0),
K3LAPIBase::K3LAPIBase()
: _device_count(0), _channel_count(0), _link_count(0),
_device_config(0), _channel_config(0), _link_config(0)
{};
/* initialize the whole thing! */
void K3LAPI::start(void)
void K3LAPIBase::start(void)
{
/* tie the used k3l to the compiled k3l version */
char *ret = k3lStart(k3lApiMajorVersion, k3lApiMinorVersion, 0); //k3lApiBuildVersion);
......@@ -64,7 +62,7 @@ void K3LAPI::start(void)
init();
}
void K3LAPI::stop(void)
void K3LAPIBase::stop(void)
{
k3lStop();
fini();
......@@ -72,7 +70,7 @@ void K3LAPI::stop(void)
/* envio de comandos para placa */
void K3LAPI::mixer(int32 dev, int32 obj, byte track, KMixerSource src, int32 index)
void K3LAPIBase::mixer(int32 dev, int32 obj, byte track, KMixerSource src, int32 index) const
{
KMixerCommand mix;
......@@ -83,7 +81,7 @@ void K3LAPI::mixer(int32 dev, int32 obj, byte track, KMixerSource src, int32 ind
command(dev, obj, CM_MIXER, (const char *) &mix);
}
void K3LAPI::mixerRecord(int32 dev, int32 obj, byte track, KMixerSource src, int32 index)
void K3LAPIBase::mixerRecord(int32 dev, KDeviceType type, int32 obj, byte track, KMixerSource src, int32 index) const
{
/* estes buffers *NAO PODEM SER ESTATICOS*! */
char cmd[] = { 0x3f, 0x03, (char)obj, (char)track, 0xff, 0xff };
......@@ -131,12 +129,12 @@ void K3LAPI::mixerRecord(int32 dev, int32 obj, byte track, KMixerSource src, int
break;
}
int32 dsp = get_dsp(dev, DSP_AUDIO);
int32 dsp = get_dsp(type, DSP_AUDIO);
raw_command(dev, dsp, cmd, sizeof(cmd));
}
void K3LAPI::mixerCTbus(int32 dev, int32 obj, byte track, KMixerSource src, int32 index)
void K3LAPIBase::mixerCTbus(int32 dev, int32 obj, byte track, KMixerSource src, int32 index) const
{
KMixerCommand mix;
......@@ -147,12 +145,12 @@ void K3LAPI::mixerCTbus(int32 dev, int32 obj, byte track, KMixerSource src, int3
command(dev, obj, CM_MIXER_CTBUS, (const char *) &mix);
}
void K3LAPI::command(int32 dev, int32 obj, int32 code, std::string & str)
void K3LAPIBase::command(int32 dev, int32 obj, int32 code, std::string & str) const
{
command(dev, obj, code, str.c_str());
}
void K3LAPI::command (int32 dev, int32 obj, int32 code, const char * parms)
void K3LAPIBase::command (int32 dev, int32 obj, int32 code, const char * parms) const
{
K3L_COMMAND cmd;
......@@ -166,12 +164,12 @@ void K3LAPI::command (int32 dev, int32 obj, int32 code, const char * parms)
throw failed_command(code, dev, obj, rc);
}
void K3LAPI::raw_command(int32 dev, int32 dsp, std::string & str)
void K3LAPIBase::raw_command(int32 dev, int32 dsp, std::string & str) const
{
raw_command(dev, dsp, str.data(), str.size());
}
void K3LAPI::raw_command(int32 dev, int32 dsp, const char * cmds, int32 size)
void K3LAPIBase::raw_command(int32 dev, int32 dsp, const char * cmds, int32 size) const
{
std::string str(cmds, size);
......@@ -181,7 +179,7 @@ void K3LAPI::raw_command(int32 dev, int32 dsp, const char * cmds, int32 size)
throw failed_raw_command(dev, dsp, rc);
}
KLibraryStatus K3LAPI::get_param(K3L_EVENT *ev, const char *name, std::string &res)
KLibraryStatus K3LAPIBase::get_param(K3L_EVENT *ev, const char *name, std::string &res) const
{
char tmp_param[256];
memset((void*)tmp_param, 0, sizeof(tmp_param));
......@@ -195,7 +193,7 @@ KLibraryStatus K3LAPI::get_param(K3L_EVENT *ev, const char *name, std::string &r
return ksSuccess;
}
std::string K3LAPI::get_param(K3L_EVENT *ev, const char *name)
std::string K3LAPIBase::get_param(K3L_EVENT *ev, const char *name) const
{
std::string res;
......@@ -207,7 +205,7 @@ std::string K3LAPI::get_param(K3L_EVENT *ev, const char *name)
return res;
}
void K3LAPI::init(void)
void K3LAPIBase::init(void)
{
if (_device_count != 0) return;
......@@ -217,20 +215,16 @@ void K3LAPI::init(void)
_device_config = new device_conf_type[_device_count];
_channel_config = new channel_ptr_conf_type[_device_count];
_link_config = new link_ptr_conf_type[_device_count];
_channel_count = new unsigned int[_device_count];
_link_count = new unsigned int[_device_count];
_channel_count = new unsigned int[_device_count];
_link_count = new unsigned int[_device_count];
for (unsigned int dev = 0; dev < _device_count; dev++)
{
KLibraryStatus ret = ksSuccess;
_device_type[dev] = (KDeviceType) k3lGetDeviceType(dev);
/* caches each device config */
ret = (KLibraryStatus)k3lGetDeviceConfig(dev, ksoDevice + dev, &(_device_config[dev]), sizeof(_device_config[dev]));
if (ret != ksSuccess)
throw start_failed(STG(FMT("k3lGetDeviceConfig(dev=%d): %s") % dev % Verbose::status(ret)));
if (k3lGetDeviceConfig(dev, ksoDevice + dev, &(_device_config[dev]), sizeof(_device_config[dev])) != ksSuccess)
throw start_failed("k3lGetDeviceConfig(device)");
/* adjust channel/link count for device */
_channel_count[dev] = _device_config[dev].ChannelCount;
......@@ -241,10 +235,9 @@ void K3LAPI::init(void)
for (unsigned int obj = 0; obj < _channel_count[dev]; obj++)
{
ret = (KLibraryStatus)k3lGetDeviceConfig(dev, ksoChannel + obj, &(_channel_config[dev][obj]), sizeof(_channel_config[dev][obj]));
if (ret != ksSuccess)
throw start_failed(STG(FMT("k3lGetDeviceConfig(dev=%d,chan=%d): %s") % dev % obj % Verbose::status(ret)));
if (k3lGetDeviceConfig(dev, ksoChannel + obj, &(_channel_config[dev][obj]),
sizeof(_channel_config[dev][obj])) != ksSuccess)
throw start_failed("k3lGetDeviceConfig(channel)");
}
/* adjust link count for device */
......@@ -255,15 +248,14 @@ void K3LAPI::init(void)
for (unsigned int obj = 0; obj < _link_count[dev]; obj++)
{
ret = (KLibraryStatus)k3lGetDeviceConfig(dev, ksoLink + obj, &(_link_config[dev][obj]), sizeof(_link_config[dev][obj]));
if (ret != ksSuccess)
throw start_failed(STG(FMT("k3lGetDeviceConfig(dev=%d,link=%d): %s") % dev % obj % Verbose::status(ret)));
if (k3lGetDeviceConfig(dev, ksoLink + obj, &(_link_config[dev][obj]),
sizeof(_link_config[dev][obj])) != ksSuccess)
throw start_failed("k3lGetDeviceConfig(link)");
}
}
}
void K3LAPI::fini(void)
void K3LAPIBase::fini(void)
{
for (unsigned int dev = 0; dev < _device_count; dev++)
{
......@@ -290,9 +282,9 @@ void K3LAPI::fini(void)
if (_link_count) { delete[] _link_count; _link_count = NULL; }
}
int32 K3LAPI::get_dsp(int32 dev, K3LAPI::DspType type)
int32 K3LAPIBase::get_dsp(KDeviceType devtype, K3LAPI::DspType type) const
{
switch (device_type(dev))
switch (devtype)
{
case kdtFXO:
case kdtFXOVoIP:
......@@ -311,3 +303,7 @@ int32 K3LAPI::get_dsp(int32 dev, K3LAPI::DspType type)
}
}
int32 K3LAPIBase::get_dsp(const K3LAPIBase::GenericTarget & tgt, K3LAPI::DspType type) const
{
return get_dsp(_device_type[tgt.device], type);
}
......@@ -46,7 +46,8 @@ std::string K3LUtil::channelStatus(int32 dev, int32 channel,
{
try
{
K3L_CHANNEL_CONFIG & config = _k3lapi.channel_config(dev, channel);
const K3L_CHANNEL_CONFIG & config = _k3lapi.channel_config(dev, channel);
K3L_CHANNEL_STATUS status;
KLibraryStatus ret = (KLibraryStatus) k3lGetDeviceStatus (dev,
......@@ -60,7 +61,7 @@ std::string K3LUtil::channelStatus(int32 dev, int32 channel,
: "Unknown (fail)");
}
}
catch(K3LAPI::invalid_channel & e)
catch(K3LAPITraits::invalid_channel & e)
{
return (fmt == Verbose::EXACT ? "<unknown[fail]>" : "Unknown (fail)");
}
......@@ -83,64 +84,35 @@ std::string K3LUtil::callStatus(int32 dev, int32 channel,
}
std::string K3LUtil::linkStatus(int32 dev, int32 link,
Verbose::Presentation fmt, KSignaling sig)
Verbose::Presentation fmt, KSignaling signaling, bool simpleStatus)
{
try
{
if (sig == ksigInactive)
if (signaling == ksigInactive)
{
K3L_LINK_CONFIG & config = _k3lapi.link_config(dev, link);
sig = config.Signaling;
const K3L_LINK_CONFIG & config = _k3lapi.link_config(dev, link);
signaling = config.Signaling;
}
K3L_LINK_STATUS status;
K3L_LINK_STATUS status;
KLibraryStatus ret = (KLibraryStatus) k3lGetDeviceStatus (dev,
link + ksoLink, &status, sizeof(status));
switch (ret)
{
case ksSuccess: return Verbose::linkStatus(sig, status.E1, fmt);
case ksSuccess: return Verbose::linkStatus(signaling, status.E1, fmt, simpleStatus);
default: return (fmt == Verbose::EXACT ?
"<unknown[failure]>" : "Unknown (failure)");
}
}
catch(K3LAPI::invalid_channel & e)
catch(K3LAPITraits::invalid_channel & e)
{
return (fmt == Verbose::EXACT ? "<unknown[failure]>"
: "Unknown (failure)");
}
}
std::string K3LUtil::getLinkStatus(int32 dev, int32 link,
Verbose::Presentation fmt)
{
switch (_k3lapi.device_type(dev))
{
#if K3L_AT_LEAST(1,6,0)
case kdtFXS:
case kdtFXSSpx:
return linkStatus(dev, link, fmt, ksigAnalogTerminal);
#if K3L_AT_LEAST(2,1,0)
case kdtE1FXSSpx:
if (link == 1)
return linkStatus(dev, link, fmt, ksigAnalogTerminal);
#endif
#endif
default:
break;
}
K3L_LINK_CONFIG & conf = _k3lapi.link_config(dev, link);
std::string res = linkStatus(dev, link, fmt);
if (conf.ReceivingClock & 0x01)
res += (fmt == Verbose::EXACT ? ",sync" : " (sync)");
return res;
}
unsigned int K3LUtil::physicalLinkCount(int32 dev, bool count_virtual)
{
......@@ -199,7 +171,7 @@ unsigned int K3LUtil::physicalLinkCount(int32 dev, bool count_virtual)
break;
}
}
catch(K3LAPI::invalid_device & e)
catch(K3LAPITraits::invalid_device & e)
{
return 0;
}
......
......@@ -64,10 +64,8 @@ struct K3LUtil
std::string linkStatus(int32, int32,
Verbose::Presentation fmt = Verbose::HUMAN,
KSignaling sig = ksigInactive);
std::string getLinkStatus(int32, int32,
Verbose::Presentation fmt = Verbose::HUMAN);
KSignaling sig = ksigInactive,
bool simpleStatus = false);
unsigned int physicalLinkCount(int32 dev, bool count_virtual = false);
......
......@@ -55,6 +55,7 @@
#if defined(COMMONS_LIBRARY_USING_ASTERISK)
extern "C"
{
#include <asterisk.h>
#include <asterisk/localtime.h>
}
#elif defined(COMMONS_LIBRARY_USING_CALLWEAVER)
......@@ -443,35 +444,17 @@ struct Logger
localtime_r (&tv, &lt);
#endif
out_msg += STG(FMT("[%04d-%02d-%02d %02d:%02d:%02d] ")
% (lt.tm_year + 1900) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour
% lt.tm_min % lt.tm_sec);
#elif defined(COMMONS_LIBRARY_USING_CALLWEAVER)
#elif defined(COMMONS_LIBRARY_USING_CALLWEAVER) || defined(COMMONS_LIBRARY_USING_FREESWITCH)
time_t tv;
struct tm lt;
time (&tv);
localtime_r (&tv, &lt);
out_msg += STG(FMT("[%04d-%02d-%02d %02d:%02d:%02d] ")
% (lt.tm_year + 1900) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour
% lt.tm_min % lt.tm_sec);
#elif defined(COMMONS_LIBRARY_USING_FREESWITCH)
time_t tv;
struct tm lt;
time (&tv);
localtime_r (&tv, &lt);
out_msg += STG(FMT("[%04d-%02d-%02d %02d:%02d:%02d] ")
% (lt.tm_year + 1900) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour
% lt.tm_min % lt.tm_sec);
#endif
out_msg += STG(FMT("[%02d-%02d-%02d %02d:%02d:%02d] ")
% (lt.tm_year % 100) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour
% lt.tm_min % lt.tm_sec);
}
if (opt._flags[Option::DATETIMEMS])
......@@ -497,28 +480,16 @@ struct Logger
#endif
#if ASTERISK_AT_LEAST(1,6,0)
out_msg += STG(FMT("[%04d-%02d-%02d %02d:%02d:%02d:%04d] ")
% (lt.tm_year + 1900) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
out_msg += STG(FMT("[%02d-%02d-%02d %02d:%02d:%02d:%04d] ")
% (lt.tm_year % 100) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
% lt.tm_sec % (tv.tv_usec / 1000));
#else
out_msg += STG(FMT("[%04d-%02d-%02d %02d:%02d:%02d:%04d] ")
% (lt.tm_year + 1900) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
out_msg += STG(FMT("[%02d-%02d-%02d %02d:%02d:%02d:%04d] ")
% (lt.tm_year % 100) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
% lt.tm_sec % (tv * 1000));
#endif
#elif defined(COMMONS_LIBRARY_USING_CALLWEAVER)
time_t tv;
struct tm lt;
time (&tv);
localtime_r (&tv, &lt);
out_msg += STG(FMT("[%04d-%02d-%02d %02d:%02d:%02d:%04d] ")
% (lt.tm_year + 1900) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
% lt.tm_sec % (tv * 1000));
#elif defined(COMMONS_LIBRARY_USING_FREESWITCH)
#elif defined(COMMONS_LIBRARY_USING_CALLWEAVER) || defined(COMMONS_LIBRARY_USING_FREESWITCH)
time_t tv;
struct tm lt;
......@@ -526,10 +497,9 @@ struct Logger
localtime_r (&tv, &lt);
out_msg += STG(FMT("[%04d-%02d-%02d %02d:%02d:%02d:%04d] ")
% (lt.tm_year + 1900) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
out_msg += STG(FMT("[%02d-%02d-%02d %02d:%02d:%02d:%04d] ")
% (lt.tm_year % 100) % (lt.tm_mon + 1) % lt.tm_mday % lt.tm_hour % lt.tm_min
% lt.tm_sec % (tv * 1000));
#endif
}
......@@ -538,7 +508,7 @@ struct Logger
if (opt._flags[Option::THREADID])
{
#if defined (COMMONS_LIBRARY_USING_ASTERISK) || defined(COMMONS_LIBRARY_USING_CALLWEAVER) || defined(COMMONS_LIBRARY_USING_FREESWITCH)
out_msg += STG(FMT("t=%08p ") % ((void*)pthread_self()));
out_msg += STG(FMT("%08x ") % ((unsigned long)pthread_self()));
#endif
}
......
......@@ -45,6 +45,11 @@
#ifndef _REFCOUNTER_HPP_
#define _REFCOUNTER_HPP_
#define COUNTER_CLASS(...) ReferenceCounter< __VA_ARGS__ >
#define COUNTER_SUPER(...) public COUNTER_CLASS( __VA_ARGS__ )
#define COUNTER_REFER(o, ...) COUNTER_CLASS( __VA_ARGS__ )(static_cast< const COUNTER_CLASS( __VA_ARGS__ ) & >(o))
// DEPRECATED DECLARATIONS ///
#define NEW_REFCOUNTER(...) public ReferenceCounter< __VA_ARGS__ >
#define INC_REFCOUNTER(o, ...) ReferenceCounter< __VA_ARGS__ >(static_cast< const ReferenceCounter < __VA_ARGS__ > & >(o))
......@@ -179,13 +184,13 @@ struct ReferenceCounter
};
template < typename T >
struct ReferenceContainer: NEW_REFCOUNTER(ReferenceContainer< T >)
struct ReferenceContainer: COUNTER_SUPER(ReferenceContainer< T >)
{
/* type */
typedef T Type;
/* shorthand */
typedef ReferenceCounter < ReferenceContainer< Type > > Counter;
typedef COUNTER_CLASS(ReferenceContainer< Type >) Counter;
// TODO: make this a generic exception someday
struct NotFound {};
......@@ -249,7 +254,7 @@ struct ReferenceContainer: NEW_REFCOUNTER(ReferenceContainer< T >)
};
// return value (pointer)!
Type * operator()(void)
Type * operator()(void) const
{
return _reference_value;
};
......
......@@ -74,7 +74,7 @@ void Regex::Expression::initialize(void)
_errorstate = regcomp(&_comp_regex, _expression, _flags);
}
std::string Regex::Expression::regerror_as_string(void)
std::string Regex::Expression::regerror_as_string(void) const
{
unsigned int count = regerror(_errorstate, &_comp_regex, 0, 0) + 1;
......@@ -95,6 +95,7 @@ void Regex::Match::initialize(void)
{
_subcounter = (_expression.subcount() + 2); // 0 + N.. + invalid
_submatches = new regmatch_t[_subcounter];
_subcaching = new std::string[_subcounter];
_have_match = (regexec(_expression.repr(), _basestring.c_str(),
_subcounter, _submatches, _flags) == 0);
}
......@@ -117,7 +118,7 @@ std::string Regex::Match::replace(Regex::ReplaceMap & map)
try
{
if (_submatches[0].rm_so != 0 && (map.find(0) != map.end()))
return _basestring.replace(_submatches[0].rm_so, _submatches[0].rm_eo - _submatches[0].rm_so, map.find(0)->second);
return buffer.replace(_submatches[0].rm_so, _submatches[0].rm_eo - _submatches[0].rm_so, map.find(0)->second);
for (unsigned int n = 1; (_submatches[n].rm_so != -1) && (n < _subcounter); n++)
{
......
......@@ -55,7 +55,7 @@
* implement the "unreference()" method for releasing resources. */
template < typename Implementor >
struct SimpleLockCommon: NEW_REFCOUNTER( SimpleLockCommon < Implementor > )
struct SimpleLockCommon: COUNTER_SUPER( SimpleLockCommon < Implementor > )
{
friend class ReferenceCounter < SimpleLockCommon < Implementor > >;
......@@ -71,7 +71,7 @@ struct SimpleLockCommon: NEW_REFCOUNTER( SimpleLockCommon < Implementor > )
{};
SimpleLockCommon(const SimpleLockCommon & o)
: INC_REFCOUNTER(o, SimpleLockCommon)
: COUNTER_REFER(o, SimpleLockCommon)
{};
virtual ~SimpleLockCommon()
......
......@@ -276,6 +276,20 @@ bool TimerTraits::traits_restart (TimerTraits::Index & idx, bool force)
return ret;
}
void TimerTraits::traits_setup(TimerTraits::Index * idx, unsigned int msecs, const void * func, void * data, unsigned int value)
{
_mutex.lock();
if (idx->valid)
{
(void)traits_del_unlocked(*idx);
}
*idx = traits_add_unlocked(msecs, func, data, value);
_mutex.unlock();
}
bool TimerTraits::traits_del_unlocked (TimerTraits::Index & idx)
{
bool ret = false;
......
......@@ -97,7 +97,7 @@ struct FrameStorage
template < int S >
struct FrameManager: protected FrameStorage
{
typedef char Packet[ S ];
typedef const char Packet[ S ];
typedef Ringbuffer < Packet > AudioBuffer;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论