当前位置:   article > 正文

c++:改造cmdline用于MSVC下的命令行参数解析_vs2015 cmdline.h参数解析 cxxabi.h

vs2015 cmdline.h参数解析 cxxabi.h

cmdline是一个轻量级的c++命令行参数解析工具,全部源码只有一个cmdline.h头文件,使用起来非常方便,关于如何使用它,不是本文讨论的重点,本文要说的是如何解决cmdline在MSVC下不能编译的问题。

你打开下面的cmdline项目托管地址只看首页上的说明就会使用了,另外,如果你看英文比较吃力,这篇博文有全面详细的中文翻译《cmdline —— 轻量级的C++命令行解析库》
cmdline项目托管地址Github:https://github.com/tanakh/cmdline

MSVC下编译报错

我在使用cmdline的时候,在gcc下编译都正常,但在MSVC环境下,是不能编译的,因为缺少头文件cxxabi.h,这个头文件MSVC是没有的。开始发现这个问题后,打算绕过它,只在gcc下编译,但因为其他原因逼得我只能想办法在MSVC下编译,所以就要解决这个问题。
怎么办呢?我尝试着注释掉cmdline.h中#include <cxxabi.h>这一行代码,发现只有一个地方报错,就是下面的函数,也就是说#include <cxxabi.h>中的函数只在这一处被用到,貌似问题不那么大,所以我就继续深入研究下去。

static inline std::string demangle(const std::string &name)
{
  int status=0;
  char *p=abi::__cxa_demangle(name.c_str(), 0, 0, &status);//用到cxxabi.h中的函数
  std::string ret(p);
  free(p);
  return ret;
}
template <class T>
std::string readable_typename() // 获取类型T名称
{
  return demangle(typeid(T).name());// 调用demangle返回类型T的真实名字
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

原因分析

C/C++语言在编译以后,函数和数据类型的名字会被编译器修改,改成编译器内部的名字,这个名字会在链接的时候用到。如果用backtrace之类的函数打印堆栈时,显示的就是被编译器修改过的名字,比如说_Z3foov ,
数据类型名称也是一样,比如在gcc下double的类型内部名字就变成了’d’,

gcc下调用typeid(double).name()返回的结果是’d’ 。

那么这个函数或类型真实的名字是什么呢?
如何在运行时获取类型或函数真实的名称呢?
上面这个demangle函数中调用的abi::__cxa_demangle的作用就是将编译器内部使用的名字反向转换(demangle)为源代码中定义的名字。
MSVC为什么没有提供abi::__cxa_demangle类似的功能呢?因为MSVC编译器编译的代码typeid返回的是demangle后的结果。
也就是说,在MSVC下typeid(double).name()返回的就是"double"。所以不需要类似的功能。

解决办法

找到原因就好办了,只需要用宏定义改造代码就好了,只需要修改两处代码
1.修改#include部分

//当编译器非gcc时,不包含cxxabi.h头文件
#ifdef __GNUC__
#include <cxxabi.h>
#endif
  • 1
  • 2
  • 3
  • 4

2.修改demangle函数,当编译器为MSVC时直接将输入参数返回

static inline std::string demangle(const std::string &name)
{
#ifdef _MSC_VER
  return name; // 为MSVC编译器时直接返回name
#elif defined(__GNUC__) 
  // 为gcc编译器时还调用原来的代码
  int status=0;
  char *p=abi::__cxa_demangle(name.c_str(), 0, 0, &status);
  std::string ret(p);
  free(p);
  return ret;
#else
// 其他不支持的编译器需要自己实现这个方法
#error unexpected c complier (msc/gcc), Need to implement this method for demangle
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

参考资料

《如何识别C++编译以后的函数名(demangle)》
《C++类名demangle》
《abi::__cxa_demangle》

修改后的cmdline完整代码:

下面是修改后的cmdline完整代码,也可以从我的gitee仓库下载
https://gitee.com/l0km/common_source_cpp/blob/master/cmdline.h

/*
  Copyright (c) 2009, Hideyuki Tanaka
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
  * Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
  * Neither the name of the <organization> nor the
  names of its contributors may be used to endorse or promote products
  derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
#include <stdexcept>
#include <typeinfo>
#include <cstring>
#include <algorithm>
//当编译器非gcc时,不包含cxxabi.h头文件
#ifdef __GNUC__
#include <cxxabi.h>
#endif
#include <cstdlib>

namespace cmdline{

namespace detail{

template <typename Target, typename Source, bool Same>
class lexical_cast_t{
public:
  static Target cast(const Source &arg){
    Target ret;
    std::stringstream ss;
    if (!(ss<<arg && ss>>ret && ss.eof()))
      throw std::bad_cast();
    
    return ret;
  }
};

template <typename Target, typename Source>
class lexical_cast_t<Target, Source, true>{
public:
  static Target cast(const Source &arg){
    return arg;
  }  
};

template <typename Source>
class lexical_cast_t<std::string, Source, false>{
public:
  static std::string cast(const Source &arg){
    std::ostringstream ss;
    ss<<arg;
    return ss.str();
  }
};

template <typename Target>
class lexical_cast_t<Target, std::string, false>{
public:
  static Target cast(const std::string &arg){
    Target ret;
    std::istringstream ss(arg);
    if (!(ss>>ret && ss.eof()))
      throw std::bad_cast();
    return ret;
  }
};

template <typename T1, typename T2>
struct is_same {
  static const bool value = false;
};

template <typename T>
struct is_same<T, T>{
  static const bool value = true;
};

template<typename Target, typename Source>
Target lexical_cast(const Source &arg)
{
  return lexical_cast_t<Target, Source, detail::is_same<Target, Source>::value>::cast(arg);
}

static inline std::string demangle(const std::string &name)
{
#ifdef _MSC_VER
  return name; // 为MSVC编译器时直接返回name
#elif defined(__GNUC__) 
  // 为gcc编译器时还调用原来的代码
  int status=0;
  char *p=abi::__cxa_demangle(name.c_str(), 0, 0, &status);
  std::string ret(p);
  free(p);
  return ret;
#else
// 其他不支持的编译器需要自己实现这个方法
#error unexpected c complier (msc/gcc), Need to implement this method for demangle
#endif
}

template <class T>
std::string readable_typename()
{
  return demangle(typeid(T).name());
}

template <class T>
std::string default_value(T def)
{
  return detail::lexical_cast<std::string>(def);
}

template <>
inline std::string readable_typename<std::string>()
{
  return "string";
}

} // detail

//-----

class cmdline_error : public std::exception {
public:
  cmdline_error(const std::string &msg): msg(msg){}
  ~cmdline_error() throw() {}
  const char *what() const throw() { return msg.c_str(); }
private:
  std::string msg;
};

template <class T>
struct default_reader{
  T operator()(const std::string &str){
    return detail::lexical_cast<T>(str);
  }
};

template <class T>
struct range_reader{
  range_reader(const T &low, const T &high): low(low), high(high) {}
  T operator()(const std::string &s) const {
    T ret=default_reader<T>()(s);
    if (!(ret>=low && ret<=high)) throw cmdline::cmdline_error("range_error");
    return ret;
  }
private:
  T low, high;
};

template <class T>
range_reader<T> range(const T &low, const T &high)
{
  return range_reader<T>(low, high);
}

template <class T>
struct oneof_reader{
  T operator()(const std::string &s){
    T ret=default_reader<T>()(s);
    if (std::find(alt.begin(), alt.end(), ret)==alt.end())
      throw cmdline_error("");
    return ret;
  }
  void add(const T &v){ alt.push_back(v); }
private:
  std::vector<T> alt;
};

template <class T>
oneof_reader<T> oneof(T a1)
{
  oneof_reader<T> ret;
  ret.add(a1);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  ret.add(a4);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  ret.add(a4);
  ret.add(a5);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  ret.add(a4);
  ret.add(a5);
  ret.add(a6);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  ret.add(a4);
  ret.add(a5);
  ret.add(a6);
  ret.add(a7);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  ret.add(a4);
  ret.add(a5);
  ret.add(a6);
  ret.add(a7);
  ret.add(a8);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  ret.add(a4);
  ret.add(a5);
  ret.add(a6);
  ret.add(a7);
  ret.add(a8);
  ret.add(a9);
  return ret;
}

template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10)
{
  oneof_reader<T> ret;
  ret.add(a1);
  ret.add(a2);
  ret.add(a3);
  ret.add(a4);
  ret.add(a5);
  ret.add(a6);
  ret.add(a7);
  ret.add(a8);
  ret.add(a9);
  ret.add(a10);
  return ret;
}

//-----

class parser{
public:
  parser(){
  }
  ~parser(){
    for (std::map<std::string, option_base*>::iterator p=options.begin();
         p!=options.end(); p++)
      delete p->second;
  }

  void add(const std::string &name,
           char short_name=0,
           const std::string &desc=""){
    if (options.count(name)) throw cmdline_error("multiple definition: "+name);
    options[name]=new option_without_value(name, short_name, desc);
    ordered.push_back(options[name]);
  }

  template <class T>
  void add(const std::string &name,
           char short_name=0,
           const std::string &desc="",
           bool need=true,
           const T def=T()){
    add(name, short_name, desc, need, def, default_reader<T>());
  }

  template <class T, class F>
  void add(const std::string &name,
           char short_name=0,
           const std::string &desc="",
           bool need=true,
           const T def=T(),
           F reader=F()){
    if (options.count(name)) throw cmdline_error("multiple definition: "+name);
    options[name]=new option_with_value_with_reader<T, F>(name, short_name, need, def, desc, reader);
    ordered.push_back(options[name]);
  }

  void footer(const std::string &f){
    ftr=f;
  }

  void set_program_name(const std::string &name){
    prog_name=name;
  }

  bool exist(const std::string &name) const {
    if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name);
    return options.find(name)->second->has_set();
  }

  template <class T>
  const T &get(const std::string &name) const {
    if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name);
    const option_with_value<T> *p=dynamic_cast<const option_with_value<T>*>(options.find(name)->second);
    if (p==NULL) throw cmdline_error("type mismatch flag '"+name+"'");
    return p->get();
  }

  const std::vector<std::string> &rest() const {
    return others;
  }

  bool parse(const std::string &arg){
    std::vector<std::string> args;

    std::string buf;
    bool in_quote=false;
    for (std::string::size_type i=0; i<arg.length(); i++){
      if (arg[i]=='\"'){
        in_quote=!in_quote;
        continue;
      }

      if (arg[i]==' ' && !in_quote){
        args.push_back(buf);
        buf="";
        continue;
      }

      if (arg[i]=='\\'){
        i++;
        if (i>=arg.length()){
          errors.push_back("unexpected occurrence of '\\' at end of string");
          return false;
        }
      }

      buf+=arg[i];
    }

    if (in_quote){
      errors.push_back("quote is not closed");
      return false;
    }

    if (buf.length()>0)
      args.push_back(buf);

    for (size_t i=0; i<args.size(); i++)
      std::cout<<"\""<<args[i]<<"\""<<std::endl;

    return parse(args);
  }

  bool parse(const std::vector<std::string> &args){
    int argc=static_cast<int>(args.size());
    std::vector<const char*> argv(argc);

    for (int i=0; i<argc; i++)
      argv[i]=args[i].c_str();

    return parse(argc, &argv[0]);
  }

  bool parse(int argc, const char * const argv[]){
    errors.clear();
    others.clear();

    if (argc<1){
      errors.push_back("argument number must be longer than 0");
      return false;
    }
    if (prog_name=="")
      prog_name=argv[0];

    std::map<char, std::string> lookup;
    for (std::map<std::string, option_base*>::iterator p=options.begin();
         p!=options.end(); p++){
      if (p->first.length()==0) continue;
      char initial=p->second->short_name();
      if (initial){
        if (lookup.count(initial)>0){
          lookup[initial]="";
          errors.push_back(std::string("short option '")+initial+"' is ambiguous");
          return false;
        }
        else lookup[initial]=p->first;
      }
    }

    for (int i=1; i<argc; i++){
      if (strncmp(argv[i], "--", 2)==0){
        const char *p=strchr(argv[i]+2, '=');
        if (p){
          std::string name(argv[i]+2, p);
          std::string val(p+1);
          set_option(name, val);
        }
        else{
          std::string name(argv[i]+2);
          if (options.count(name)==0){
            errors.push_back("undefined option: --"+name);
            continue;
          }
          if (options[name]->has_value()){
            if (i+1>=argc){
              errors.push_back("option needs value: --"+name);
              continue;
            }
            else{
              i++;
              set_option(name, argv[i]);
            }
          }
          else{
            set_option(name);
          }
        }
      }
      else if (strncmp(argv[i], "-", 1)==0){
        if (!argv[i][1]) continue;
        char last=argv[i][1];
        for (int j=2; argv[i][j]; j++){
          last=argv[i][j];
          if (lookup.count(argv[i][j-1])==0){
            errors.push_back(std::string("undefined short option: -")+argv[i][j-1]);
            continue;
          }
          if (lookup[argv[i][j-1]]==""){
            errors.push_back(std::string("ambiguous short option: -")+argv[i][j-1]);
            continue;
          }
          set_option(lookup[argv[i][j-1]]);
        }

        if (lookup.count(last)==0){
          errors.push_back(std::string("undefined short option: -")+last);
          continue;
        }
        if (lookup[last]==""){
          errors.push_back(std::string("ambiguous short option: -")+last);
          continue;
        }

        if (i+1<argc && options[lookup[last]]->has_value()){
          set_option(lookup[last], argv[i+1]);
          i++;
        }
        else{
          set_option(lookup[last]);
        }
      }
      else{
        others.push_back(argv[i]);
      }
    }

    for (std::map<std::string, option_base*>::iterator p=options.begin();
         p!=options.end(); p++)
      if (!p->second->valid())
        errors.push_back("need option: --"+std::string(p->first));

    return errors.size()==0;
  }

  void parse_check(const std::string &arg){
    if (!options.count("help"))
      add("help", '?', "print this message");
    check(0, parse(arg));
  }

  void parse_check(const std::vector<std::string> &args){
    if (!options.count("help"))
      add("help", '?', "print this message");
    check(args.size(), parse(args));
  }

  void parse_check(int argc, char *argv[]){
    if (!options.count("help"))
      add("help", '?', "print this message");
    check(argc, parse(argc, argv));
  }

  std::string error() const{
    return errors.size()>0?errors[0]:"";
  }

  std::string error_full() const{
    std::ostringstream oss;
    for (size_t i=0; i<errors.size(); i++)
      oss<<errors[i]<<std::endl;
    return oss.str();
  }

  std::string usage() const {
    std::ostringstream oss;
    oss<<"usage: "<<prog_name<<" ";
    for (size_t i=0; i<ordered.size(); i++){
      if (ordered[i]->must())
        oss<<ordered[i]->short_description()<<" ";
    }
    
    oss<<"[options] ... "<<ftr<<std::endl;
    oss<<"options:"<<std::endl;

    size_t max_width=0;
    for (size_t i=0; i<ordered.size(); i++){
      max_width=std::max(max_width, ordered[i]->name().length());
    }
    for (size_t i=0; i<ordered.size(); i++){
      if (ordered[i]->short_name()){
        oss<<"  -"<<ordered[i]->short_name()<<", ";
      }
      else{
        oss<<"      ";
      }

      oss<<"--"<<ordered[i]->name();
      for (size_t j=ordered[i]->name().length(); j<max_width+4; j++)
        oss<<' ';
      oss<<ordered[i]->description()<<std::endl;
    }
    return oss.str();
  }

private:

  void check(int argc, bool ok){
    if ((argc==1 && !ok) || exist("help")){
      std::cerr<<usage();
      exit(0);
    }

    if (!ok){
      std::cerr<<error()<<std::endl<<usage();
      exit(1);
    }
  }

  void set_option(const std::string &name){
    if (options.count(name)==0){
      errors.push_back("undefined option: --"+name);
      return;
    }
    if (!options[name]->set()){
      errors.push_back("option needs value: --"+name);
      return;
    }
  }

  void set_option(const std::string &name, const std::string &value){
    if (options.count(name)==0){
      errors.push_back("undefined option: --"+name);
      return;
    }
    if (!options[name]->set(value)){
      errors.push_back("option value is invalid: --"+name+"="+value);
      return;
    }
  }

  class option_base{
  public:
    virtual ~option_base(){}

    virtual bool has_value() const=0;
    virtual bool set()=0;
    virtual bool set(const std::string &value)=0;
    virtual bool has_set() const=0;
    virtual bool valid() const=0;
    virtual bool must() const=0;

    virtual const std::string &name() const=0;
    virtual char short_name() const=0;
    virtual const std::string &description() const=0;
    virtual std::string short_description() const=0;
  };

  class option_without_value : public option_base {
  public:
    option_without_value(const std::string &name,
                         char short_name,
                         const std::string &desc)
      :nam(name), snam(short_name), desc(desc), has(false){
    }
    ~option_without_value(){}

    bool has_value() const { return false; }

    bool set(){
      has=true;
      return true;
    }

    bool set(const std::string &){
      return false;
    }

    bool has_set() const {
      return has;
    }

    bool valid() const{
      return true;
    }

    bool must() const{
      return false;
    }

    const std::string &name() const{
      return nam;
    }

    char short_name() const{
      return snam;
    }

    const std::string &description() const {
      return desc;
    }

    std::string short_description() const{
      return "--"+nam;
    }

  private:
    std::string nam;
    char snam;
    std::string desc;
    bool has;
  };

  template <class T>
  class option_with_value : public option_base {
  public:
    option_with_value(const std::string &name,
                      char short_name,
                      bool need,
                      const T &def,
                      const std::string &desc)
      : nam(name), snam(short_name), need(need), has(false)
      , def(def), actual(def) {
      this->desc=full_description(desc);
    }
    ~option_with_value(){}

    const T &get() const {
      return actual;
    }

    bool has_value() const { return true; }

    bool set(){
      return false;
    }

    bool set(const std::string &value){
      try{
        actual=read(value);
        has=true;
      }
      catch(const std::exception &){
        return false;
      }
      return true;
    }

    bool has_set() const{
      return has;
    }

    bool valid() const{
      if (need && !has) return false;
      return true;
    }

    bool must() const{
      return need;
    }

    const std::string &name() const{
      return nam;
    }

    char short_name() const{
      return snam;
    }

    const std::string &description() const {
      return desc;
    }

    std::string short_description() const{
      return "--"+nam+"="+detail::readable_typename<T>();
    }

  protected:
    std::string full_description(const std::string &desc){
      return
        desc+" ("+detail::readable_typename<T>()+
        (need?"":" [="+detail::default_value<T>(def)+"]")
        +")";
    }

    virtual T read(const std::string &s)=0;

    std::string nam;
    char snam;
    bool need;
    std::string desc;

    bool has;
    T def;
    T actual;
  };

  template <class T, class F>
  class option_with_value_with_reader : public option_with_value<T> {
  public:
    option_with_value_with_reader(const std::string &name,
                                  char short_name,
                                  bool need,
                                  const T def,
                                  const std::string &desc,
                                  F reader)
      : option_with_value<T>(name, short_name, need, def, desc), reader(reader){
    }

  private:
    T read(const std::string &s){
      return reader(s);
    }

    F reader;
  };

  std::map<std::string, option_base*> options;
  std::vector<option_base*> ordered;
  std::string ftr;

  std::string prog_name;
  std::vector<std::string> others;

  std::vector<std::string> errors;
};

} // cmdline

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726
  • 727
  • 728
  • 729
  • 730
  • 731
  • 732
  • 733
  • 734
  • 735
  • 736
  • 737
  • 738
  • 739
  • 740
  • 741
  • 742
  • 743
  • 744
  • 745
  • 746
  • 747
  • 748
  • 749
  • 750
  • 751
  • 752
  • 753
  • 754
  • 755
  • 756
  • 757
  • 758
  • 759
  • 760
  • 761
  • 762
  • 763
  • 764
  • 765
  • 766
  • 767
  • 768
  • 769
  • 770
  • 771
  • 772
  • 773
  • 774
  • 775
  • 776
  • 777
  • 778
  • 779
  • 780
  • 781
  • 782
  • 783
  • 784
  • 785
  • 786
  • 787
  • 788
  • 789
  • 790
  • 791
  • 792
  • 793
  • 794
  • 795
  • 796
  • 797
  • 798
  • 799
  • 800
  • 801
  • 802
  • 803
  • 804
  • 805
  • 806
  • 807
  • 808
  • 809
  • 810
  • 811
  • 812
  • 813
  • 814
  • 815
  • 816
  • 817
  • 818
  • 819
  • 820
  • 821
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/65255
推荐阅读
相关标签
  

闽ICP备14008679号