English 中文(简体)
增强精神 页: 1
原标题:boost spirit x3 - parse tokens in any order
最佳回答

我感到,我必须提及我们,而不是查塔戈。 顺便提一下,我喜欢进行X3轮.演习。

首先,请指出,“精神 Qi”有一个从箱子上靠近的传导器:

https://coliru.stacked-crooked.com/a/0ad91f352334a2cc”rel=“nofollow noreferer”>Live On Coliru

#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
#include <iostream>

namespace qi = boost::spirit::qi;

namespace ast {
    struct cmd1 { double param1, param2; };
    struct cmd2 { std::string param1; };

    using Command = boost::variant<cmd1, cmd2>;
    using boost::fusion::operator<<;
} // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::cmd1, param1, param2)
BOOST_FUSION_ADAPT_STRUCT(ast::cmd2, param1)

template <typename It> struct CommandParser : qi::grammar<It, ast::Command()> {
    CommandParser() : CommandParser::base_type(start) {
        using namespace qi;
        quoted_string = lexeme[ "  >> *~char_( " ) >>  " ];

        cmd1  = lit("cmd1") >> ((lit("param1") >>  =  >> double_) ^ //
                               (lit("param2") >>  =  >> double_));
        cmd2  = lit("cmd2") >> ((lit("param1") >>  =  >> quoted_string));
        start = qi::skip(qi::space)[cmd1 | cmd2];

        BOOST_SPIRIT_DEBUG_NODES((cmd1)(cmd2)(start))
    }

  private:
    using Skipper = qi::space_type;
    qi::rule<It, ast::Command()>          start;
    qi::rule<It, ast::cmd1(), Skipper>    cmd1;
    qi::rule<It, ast::cmd2(), Skipper>    cmd2;
    qi::rule<It, std::string(), Skipper>  quoted_string;
};

template <typename It> boost::optional<ast::Command> parse_line(It first, It last) {
    static CommandParser<It> const p;
    ast::Command attr;

    // if (phrase_parse(first, last, qi::expect[parser::command >> qi::eoi], qi::space, attr))
    if (phrase_parse(first, last, p >> qi::eoi, qi::space, attr))
        return attr;
    return {};
}

auto parse_line(std::string_view input) { return parse_line(begin(input), end(input)); }

int main() {
    // for (std::string line; getline(std::cin, line) && !line.empty();) {
    for (std::string line :
         {
             R"()",
             R"(cmd1 param1 = 3.14 param2 = 8e-9)",
             R"(cmd1 param2 = 8e-9 param1 = 3.14)", // flipped order
             R"(cmd1 param1 = 3.14 param2 = -inf)",
             R"(cmd1 param2 = -inf param1 = 3.14)", // flipped order
             R"(cmd2 param1 = " hello world " )",

             // things that would not have parsed with question code:
             R"(cmd2 param1 = "" )",

             // things that should not parse
             R"(cmd2 param1 = 3.14 param2 = 8e-9)",
             R"(cmd1 param1 = " hello world " )",
             R"(cmd2 param1 = "" trailing rubbish)",
             R"(trailing rubbish)",
         }) //
    {
        std::cout << std::left << std::setw(40) << quoted(line);
        try {
            auto parsed = parse_line(line);
            std::cout << " -> " << parsed << std::endl;
        } catch (std::exception const& e) {
            std::cout << " -> ERROR " << e.what() << std::endl;
        }
    }
}

印刷

""                                       -> --
"cmd1 param1 = 3.14 param2 = 8e-9"       ->  (3.14 8e-09)
"cmd1 param2 = 8e-9 param1 = 3.14"       ->  (3.14 8e-09)
"cmd1 param1 = 3.14 param2 = -inf"       ->  (3.14 -inf)
"cmd1 param2 = -inf param1 = 3.14"       ->  (3.14 -inf)
"cmd2 param1 = " hello world " "       ->  ( hello world )
"cmd2 param1 = "" "                    ->  ()
"cmd2 param1 = 3.14 param2 = 8e-9"       -> --
"cmd1 param1 = " hello world " "       -> --
"cmd2 param1 = "" trailing rubbish"    -> --
"trailing rubbish"                       -> --

您可以考虑为此与Qim站在一起。

Other Approaches

为了在X3中找到类似情况,将需要一些英雄。 让我尝试

  • ignoring missing or repeated assignments (cmd1 param1 = 3 param1 = 4 is fine)
  • using latest PFR additions for c++20 member name reflection
  • dropping the need for/use of Fusion adaptation

Spirit x3: parse into ructs, and specified the worka href=” https://stackoverflow.com/questions/45673378/boost-spirit-x3-parse-into-structs/45683933#comment135826578_45683933" 评论

这里是快速和慷慨的英雄:

namespace detail {
    template <typename Attr> auto member_parser = x3::eps;
    template <>
    auto member_parser<std::string> = x3::rule<struct quoted_string, std::string>{"quoted_string"} =
        x3::lexeme[ "  >> *~x3::char_( " ) >>  " ];

    template <> auto member_parser<double> = x3::double_;

    template <size_t II, typename T, typename Tuple> auto handle_member(Tuple const& tied) {
        auto&&      val = std::get<II>(tied);
        std::string name{boost::pfr::get_name<II, T>()};

        using Attr = std::decay_t<decltype(val)>;

        auto assign = [name](auto& ctx) { boost::pfr::get<II>(*x3::get<T>(ctx)) = _attr(ctx); };
        return x3::rule<struct _>{name.c_str()} = (x3::lit(name) >>  =  >> member_parser<Attr>)[assign];
    }

    template <typename T, typename Tuple, size_t... I>
    auto params_impl(Tuple const& tied, std::integer_sequence<size_t, I...>) {
        return *(handle_member<I, T, Tuple>(tied) | ...);
    }
} // namespace detail

template <typename T> auto make_parser(T const& v = {}) {
    std::string tname = boost::typeindex::type_id<T>().pretty_name();
    tname             = tname.substr(tname.find_last_of(":") + 1);
    std::cout << "---- " << tname << std::endl;

    auto set_context = [](auto& ctx) { x3::get<T>(ctx) = &_val(ctx); };

    return x3::rule<struct _, T>{tname.c_str()} = //
        x3::with<T>(static_cast<T*>(nullptr))     //
            [x3::eps[set_context]                 //
             >> x3::lit(tname)                    //
             >> detail::params_impl<T>(boost::pfr::structure_tie(v),
                                       std::make_index_sequence<boost::pfr::tuple_size<T>::value>{})];
}

我也许会将其清理起来,使用静态的电动,而不是要求不施工,但为了加快速度,可以保持这种状态。 现在使用:

namespace parser {
    auto const command = make_parser<ast::cmd1>() | make_parser<ast::cmd2>();
} // namespace parser

或者说,如果有更多的工厂帮助:

template <typename... Cmd> auto commands() { return (make_parser<Cmd>() | ...); }

auto const command = commands<ast::cmd1, ast::cmd2>();

实例测试案例:

<>Live On Coliru

#include <boost/pfr.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/type_index.hpp>
#include <iomanip>
#include <iostream>
#include <optional>

namespace x3 = boost::spirit::x3;

namespace ast {
    struct cmd1 { double param1, param2; };
    struct cmd2 { std::string param1; };

    using Command = boost::variant<cmd1, cmd2>;
} // namespace ast

namespace parser {
    namespace detail {
        template <typename Attr> auto member_parser = x3::eps;
        template <>
        auto member_parser<std::string> = x3::rule<struct quoted_string, std::string>{"quoted_string"} =
            x3::lexeme[ "  >> *~x3::char_( " ) >>  " ];

        template <> auto member_parser<double> = x3::double_;

        template <size_t II, typename T, typename Tuple> auto handle_member(Tuple const& tied) {
            auto&&      val = std::get<II>(tied);
            std::string name{boost::pfr::get_name<II, T>()};

            using Attr = std::decay_t<decltype(val)>;

            auto assign = [name](auto& ctx) { boost::pfr::get<II>(*x3::get<T>(ctx)) = _attr(ctx); };
            return x3::rule<struct _>{name.c_str()} = (x3::lit(name) >>  =  >> member_parser<Attr>)[assign];
        }

        template <typename T, typename Tuple, size_t... I>
        auto params_impl(Tuple const& tied, std::integer_sequence<size_t, I...>) {
            return *(handle_member<I, T, Tuple>(tied) | ...);
        }
    } // namespace detail

    template <typename T> auto make_parser(T const& v = {}) {
        std::string tname = boost::typeindex::type_id<T>().pretty_name();
        tname             = tname.substr(tname.find_last_of(":") + 1);

        auto set_context = [](auto& ctx) { x3::get<T>(ctx) = &_val(ctx); };

        return x3::rule<struct _, T>{tname.c_str()} = //
            x3::with<T>(static_cast<T*>(nullptr))     //
                [x3::eps[set_context]                 //
                 >> x3::lit(tname)                    //
                 >> detail::params_impl<T>(boost::pfr::structure_tie(v),
                                           std::make_index_sequence<boost::pfr::tuple_size<T>::value>{})];
    }

    template <typename... Cmd> auto commands() { return (make_parser<Cmd>() | ...); }

    auto const command = commands<ast::cmd1, ast::cmd2>();
} // namespace parser

template <typename It> std::optional<ast::Command> parse_line(It first, It last) {
    ast::Command attr;

    // if (phrase_parse(first, last, x3::expect[parser::command >> x3::eoi], x3::space, attr))
    if (phrase_parse(first, last, parser::command >> x3::eoi, x3::space, attr))
        return attr;
    return std::nullopt;
}

auto parse_line(std::string_view input) { return parse_line(begin(input), end(input)); }

int main() {
    // for (std::string line; getline(std::cin, line) && !line.empty();) {
    for (std::string line :
         {
             R"()",
             R"(cmd1 param1 = 3.14 param2 = 8e-9)",
             R"(cmd1 param2 = 8e-9 param1 = 3.14)", // flipped
             R"(cmd1 param1 = 3.14 param2 = -inf)",
             R"(cmd1 param2 = -inf param1 = 3.14)", // flipped
             R"(cmd2 param1 = " hello world " )",

             // things that would not have parsed with question code:
             R"(cmd2 param1 = "" )",

             // things that should not parse
             R"(cmd2 param1 = 3.14 param2 = 8e-9)",
             R"(cmd1 param1 = " hello world " )",
             R"(cmd2 param1 = "" trailing rubbish)",
             R"(trailing rubbish)",
         }) //
    {
        std::cout << std::left << std::setw(37) << quoted(line);
        try {
            if (auto parsed = parse_line(line)) {
                apply_visitor(
                    [](auto const& cmd) {
                        std::cout << " -> " << boost::typeindex::type_id_runtime(cmd).pretty_name()
                                  << boost::pfr::io(cmd) << std::endl;
                    },
                    *parsed);
            } else {
                std::cout << " -> --" << std::endl;
            }
        } catch (std::exception const& e) {
            std::cout << " -> ERROR " << e.what() << std::endl;
        }
    }
}

印刷

""                                    -> --
"cmd1 param1 = 3.14 param2 = 8e-9"    -> ast::cmd1{3.14, 8e-09}
"cmd1 param2 = 8e-9 param1 = 3.14"    -> ast::cmd1{3.14, 8e-09}
"cmd1 param1 = 3.14 param2 = -inf"    -> ast::cmd1{3.14, -inf}
"cmd1 param2 = -inf param1 = 3.14"    -> ast::cmd1{3.14, -inf}
"cmd2 param1 = " hello world " "    -> ast::cmd2{" hello world "}
"cmd2 param1 = "" "                 -> ast::cmd2{""}
"cmd2 param1 = 3.14 param2 = 8e-9"    -> --
"cmd1 param1 = " hello world " "    -> --
"cmd2 param1 = "" trailing rubbish" -> --
"trailing rubbish"                    -> --

Summarizing

I would probably make a general grammar and AST like

enum class CmdType { cmd1, cmd2, ... };
using Param = std::string;
using Value = variant<double, std::string>;
using Args  = std::multimap<Param, Value>;

struct Cmd {
    CmdType cmd;
    Args    args;
};

And create a validator function that validates the correctness of the commands after parsing. This way you get a very simple grammar that s easy to maintain, and way more flexibility regarding validation logic.

问题回答

不是另一个答案——只是试图按照@sehe的建议,将碎块推向四分五裂,然后是双对等。

#include <bits/stdc++.h>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

namespace client::ast
{
    enum class CmdType : int {
        Cmd1,
        Cmd2,
        Cmd3,
        Cmd4
    };

    using Arg = boost::variant<std::string, double, int, bool>;
    using Args = std::map<std::string, Arg>;
    struct Command
    {
        CmdType m_cmdType;
        Args m_args;
    };

    using boost::fusion::operator<<;
}

BOOST_FUSION_ADAPT_STRUCT(client::ast::Command, m_cmdType, m_args);

namespace parser {
    /// write a x3 parser for a command with the following syntax:
    ///   <cmd> <param1>=<value1> <param2>=<value2>....
    /// where <cmd> is one of the enum values in client::Command
    //// param1..n are strings
    /// value1..n are <string, double, int, bool> values
    struct CmdTypeTable : x3::symbols<client::ast::CmdType> {
        CmdTypeTable() {
            add
                ("Cmd1", client::ast::CmdType::Cmd1)
                ("Cmd2", client::ast::CmdType::Cmd2)
                ("Cmd3", client::ast::CmdType::Cmd3)
                ("Cmd4", client::ast::CmdType::Cmd4)
            ;
        }
    } CmdTypeParser;

    x3::rule<class cmd_class, client::ast::Command> const cmd = "Command";

    auto const quoted_string = x3::lexeme[ "  >> *~x3::char_( " ) >>  " ];
    auto const cmd_def = CmdTypeParser >> *(x3::lexeme[+~x3::char_(" =")] >>  =  >> (quoted_string | x3::double_ | x3::int_ | x3::bool_));
    BOOST_SPIRIT_DEFINE(cmd);
}

////////////////////////////////////////////////////////////////////////////
//  Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
    std::cout << "/////////////////////////////////////////////////////////

";
    std::cout << "Command with the following syntax:
";
    std::cout << "    <cmd> <param1>=<value1> <param2>=<value2>....

";
    std::cout << "/////////////////////////////////////////////////////////

";
    std::cout << "Type [q or Q] to quit

";
    std::string str;
    while (getline(std::cin, str))
    {
        if (str.empty() || str[0] ==  q  || str[0] ==  Q )
            break;

        client::ast::Command cmd;
        auto iter = str.begin();
        auto end = str.end();
        bool r = x3::phrase_parse(iter, end, parser::cmd, ascii::space, cmd);

        if (r && iter == end)
        {
            std::cout << "-------------------------
";
            std::cout << "Parsing succeeded
";
            std::ranges::for_each(cmd.m_args, [](auto const &arg) {
                std::cout << arg.first << " = " << arg.second << "
";
            });
            std::cout << "
-------------------------
";
        }
        else
        {
            std::cout << "-------------------------
";
            std::cout << "Parsing failed
";
            std::cout << "-------------------------
";
        }
    }

    std::cout << "Bye... :-) 

";
    return 0;
}

The following code, when defined(USE_CHK_REASSIGN) assures that parameters are only assigned once, and all parameters are assigned in any order.

//OriginalSource:
//  From:
//    https://stackoverflow.com/questions/77700875/boost-spirit-x3-parse-tokens-in-any-order
//  By:
//    https://stackoverflow.com/users/16141266/chesslover
//===============
//#define USE_CHK_REASSIGN
#ifdef USE_CHK_REASSIGN
  #include <boost/optional/optional_io.hpp>
#endif
#include <boost/variant.hpp>
#include <iostream>
#include <string_view>
#include <boost/fusion/include/make_map.hpp>
  template
  < typename... Keys
  , typename... Vals
  >
  std::ostream&
operator<<
  ( std::ostream&os
  , boost::fusion::map
    < boost::fusion::pair
      < Keys
      , Vals
      >...
    >const&map
  )
  {
  ; auto const pair_print=[]
      < typename Key
      >
      ( std::ostream&o
      , auto const&m
      )
      { o<<Key{}<<"->"<<boost::fusion::at_key<Key>(m)<< 
 
      ;};
  ; ( ... , pair_print.template operator()<Keys>(os,map))
  ; return os
  ;}
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>

namespace fusion = boost::fusion;
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;

namespace ast
  {
    enum class keys_enum
      : unsigned
      { key0
      , key1
      };
    constexpr unsigned num_keys
      = 1+(unsigned)keys_enum::key1
      ;
      static constexpr std::string_view 
    key_names[num_keys]=
      { "key0"
      , "key1"
      };
      template
      < keys_enum KeyEnum
      >
    struct keys_type
      {
            friend
          std::ostream&
        operator<<
          ( std::ostream&os
          , keys_type<KeyEnum>
          )
          { return os<<key_names[(unsigned)KeyEnum]
          ;}
      #ifdef USE_CHK_REASSIGN
          template
          < typename Context
          >
          void
        operator()(Context const&ctx)
          { 
          ; std::cout<<"reassign check:KeyEnum="<<key_names[(unsigned)KeyEnum]<<";
"
          ; auto&val=fusion::at_key<keys_type>(x3::_val(ctx))
          ; std::cout<<":_val="<<val<<";
"
          ; if(val)
            { std::cout<<":already has value:
"
            ; _pass(ctx)=false
            ;}
            else
            { auto const&attr=x3::_attr(ctx)
            ; std::cout<<":_attr="<<attr<<";
"
            ; val = attr
            ;}
          ;}
      #endif//USE_CHK_REASSIGN
      };
    using key0_attr
      = keys_type<keys_enum::key0>
      ;
    using key1_attr
      = keys_type<keys_enum::key1>
      ;
  }//ast
namespace parser 
  {
    auto key0_def
      =  x3::lit("key0")
      >> x3::attr(ast::key0_attr())
      ;
    auto key1_def
      =  x3::lit("key1")
      >> x3::attr(ast::key1_attr())
      ;
    auto val0_def
      = x3::int_
      ;
    auto val1_def
      = x3::upper
      ;
    auto key_val_def
      = key0_def >> val0_def
      #ifdef USE_CHK_REASSIGN
        [ ast::keys_type<ast::keys_enum::key0>()
        ] 
      #endif//USE_CHK_REASSIGN
      | key1_def >> val1_def
      #ifdef USE_CHK_REASSIGN
        [ ast::keys_type<ast::keys_enum::key1>()
        ] 
      #endif//USE_CHK_REASSIGN
      ;
    auto key_vals_def
      = x3::repeat(ast::num_keys)[key_val_def]
      ;
    auto map_key_vals_def
      = key_vals_def
      ;
  }//parser
namespace ast
  {
      using
    val0_attr = typename
      x3::traits::attribute_of
      < decltype(parser::val0_def)
      , x3::unused_type
      >::type
      ;
      using
    val1_attr = typename
      x3::traits::attribute_of
      < decltype(parser::val1_def)
      , x3::unused_type
      >::type
      ;
      using 
    map_key_vals_attr = 
      decltype
      ( fusion::make_map
        < key0_attr
        , key1_attr
        >
      #ifdef USE_CHK_REASSIGN
        ( boost::optional<val0_attr>{}
        , boost::optional<val1_attr>{}
      #else
        ( val0_attr{}
        , val1_attr{}
      #endif
        )
      )
      ;
      using
    key_val_attr = typename
      x3::traits::attribute_of
      < decltype(parser::key_val_def)
      , x3::unused_type
      >::type
      ;
      using
    key_vals_attr = typename
      x3::traits::attribute_of
      < decltype(parser::key_vals_def)
      , x3::unused_type
      >::type
      ;
  }//ast
namespace parser 
  {
    #ifdef USE_CHK_REASSIGN
      auto map_key_vals
          //"immediate" rule to get rule attribute stored into context
          //so it can be accessed by semantic actions.
        = x3::rule<class map_key_vals_rule,ast::map_key_vals_attr>{"map_key_vals_rule"}
        = map_key_vals_def
        ;
    #endif//USE_CHK_REASSIGN
  }//parser
////////////////////////////////////////////////////////////////////////////
//  Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
  {//tests
    for 
    ( std::string str
    : { R"(key0 5 key1 C key1 D)"
      , R"(key0 6 key1 E)"
      , R"(key1 F key0 7)"
      , R"(key1 G)"
      }
    )
    {
        std::cout<<":str="<<str<<";
";
        ast::map_key_vals_attr map_key_vals_val;
        auto iter = str.begin();
        auto end = str.end();
        bool result = 
          x3::phrase_parse
          ( iter, end
        #ifdef USE_CHK_REASSIGN
          , parser::map_key_vals
        #else 
          , parser::map_key_vals_def
        #endif//USE_CHK_REASSIGN
          , ascii::space
          , map_key_vals_val
          );
        bool at_end = (iter == end);
        std::cout << ":map_key_vals_val=
";
        std::cout<<map_key_vals_val<<";
";
        if (result && at_end)
        {
            std::cout << "-------------------------
";
            std::cout << "Parsing succeeded
";
            std::cout << "
-------------------------
";
        }
        else
        {
            std::cout << "-------------------------
";
            std::cout << "Parsing failed
";
            std::cout << "result="<< result <<"
";
            std::cout << "at_end="<< at_end <<"
";
            if(!at_end)
            { std::string remaining_input(iter,end)
            ; std::cout << "remaining input="<<remaining_input<<"
"
            ;}
            std::cout << "-------------------------
";
        }
    }
  }//tests
  return 0;
}

上述法典的产出是:

:str=key0 5 key1 C key1 D;
reassign check:KeyEnum=key0;
:_val=--;
:_attr=5;
reassign check:KeyEnum=key1;
:_val=--;
:_attr=C;
:map_key_vals_val=
key0-> 5
key1-> C
;
-------------------------
Parsing failed
result=1
at_end=0
remaining input=key1 D
-------------------------
:str=key0 6 key1 E;
reassign check:KeyEnum=key0;
:_val=--;
:_attr=6;
reassign check:KeyEnum=key1;
:_val=--;
:_attr=E;
:map_key_vals_val=
key0-> 6
key1-> E
;
-------------------------
Parsing succeeded

-------------------------
:str=key1 F key0 7;
reassign check:KeyEnum=key1;
:_val=--;
:_attr=F;
reassign check:KeyEnum=key0;
:_val=--;
:_attr=7;
:map_key_vals_val=
key0-> 7
key1-> F
;
-------------------------
Parsing succeeded

-------------------------
:str=key1 G;
reassign check:KeyEnum=key1;
:_val=--;
:_attr=G;
:map_key_vals_val=
key0->--
key1-> G
;
-------------------------
Parsing failed
result=0
at_end=0
remaining input=key1 G
-------------------------

Compiler是家族式17.0.1版,有:

 -c -O0 -g -ggdb -ftemplate-backtrace-limit=0 -O0 -g -ggdb  -std=c++2b -ftemplate-backtrace-limit=0 -fdiagnostics-show-template-tree -fno-elide-type -fmacro-backtrace-limit=0 -fexceptions -fcxx-exceptions -Wno-deprecated




相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?

热门标签