当没有提供参数时,Ruby 的 *args 会导致段错误

发布于 2024-09-09 05:42:52 字数 1140 浏览 10 评论 0原文

我正在尝试使用 Ruby 在 Windows 中进行一些脚本化 GUI 测试。我正在利用 pragprog 书籍的指导我遇到了一些障碍。我提取了一些代码来帮助重复映射 win32api 函数。代码如下:

module WindowsGui

  def self.def_api(function, parameters, return_value)              
    api = Win32API.new 'user32', function, parameters, return_value

    define_method(function.snake_case) do |*args|                  
      api.call *args                                                
    end

  end
end

...所以我可以使用该块以以下方式在模块中定义多个 win32API:

def_api 'FindWindow', ['P', 'P'], 'L' 

我进行了一些 RSpec 测试以确保其行为符合预期。

  it 'wraps a Windows call with a method' do
    find_window(nil, nil).should_not == 0
  end
  
  it 'enforces the argument count' do
    lambda {find_window}.should raise_error
  end

第一次测试一切正常,第二次测试则导致段错误。似乎如果我在没有参数的情况下调用它,我就无法触摸 *args - 但我可以执行 args.length 来查看它是空的。

为什么这会导致段错误而不是异常?通过类似的方式解决这个问题是否可以接受。

  raise BadArgs if args.length == 0

另一个太长的问题 - 对此感到抱歉。

蒂亚! 鲍勃

I'm trying to do some scripted GUI testing in Windows using Ruby. I'm leveraging the guidance of a pragprog book and I'm hitting a bit of a snag. I've factored out some code to help with the repeatitive mapping of win32api functions. The code is as follows:

module WindowsGui

  def self.def_api(function, parameters, return_value)              
    api = Win32API.new 'user32', function, parameters, return_value

    define_method(function.snake_case) do |*args|                  
      api.call *args                                                
    end

  end
end

...So I can use that block to define several win32APIs in the module in the following fashion:

def_api 'FindWindow', ['P', 'P'], 'L' 

I've got some RSpec tests to ensure this behaves as it should.

  it 'wraps a Windows call with a method' do
    find_window(nil, nil).should_not == 0
  end
  
  it 'enforces the argument count' do
    lambda {find_window}.should raise_error
  end

Everything works fine as for the first test, the second results in a seg fault. Seems that if I call this with no args, I can't touch *args - but I can do a args.length to see that it's empty.

Why would this result in a seg fault and not an exception? Would it be acceptable to resolve this through something like..

  raise BadArgs if args.length == 0

Yet another question that is way too long - sorry for that.

TIA!
Bob

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

谎言 2024-09-16 05:42:52

出现段错误的可能原因是在 Win32API 下的某处,缺少参数会转化为一个或多个 NULL 指针取消引用。

如果您要创建这样的函数,那么您应该考虑添加一个每次 api 调用、返回布尔值的参数验证块(或 lambda)作为 def_api 的参数,然后将其作为派生函数的第一部分。像这样的事情:

def self.def_api(function, parameters, return_value, &valid_args?)              
  api = Win32API.new 'user32', function, parameters, return_value

  define_method(function.snake_case) do |*args|
    if valid_args? and valid_args?.call(*args)               
      api.call *args
    else
      // raise argument error, etc
    end                                         
  end

end

然后

def_api 'FindWindow', ['P', 'P'], 'L', { |args| return true if // API specific checks succeed }

更新:根据提问者的要求添加更多颜色

&valid_args? 是块参数的名称。和符号前缀 (&) 是告诉 ruby​​ 您传递了一个块的方式。您只能将一个块传递给方法,并且它必须是参数列表中的最后一个参数。问号后缀 (?) 只是 Ruby 编程中用于命名返回布尔值的函数的约定。

如果传递了一个块,则可以使用 &block.call(args)

调用它 要使用块参数调用方法,您可以执行如下操作:

method { |args| // block implementation }

或者

method do |args|
  // block implementation
end

传递 args通过调用方法到块。希望这有帮助。

The likely reason it's seg faulting is because somewhere underneath Win32API the lack of arguments is translating into one or more NULL pointer dereferences.

If you are going to create functions like this then you should consider adding a per-api-call, boolean-returning argument validation block (or lambda) as an argument to def_api which you would then invoke as the first part of your derived function. Something like this:

def self.def_api(function, parameters, return_value, &valid_args?)              
  api = Win32API.new 'user32', function, parameters, return_value

  define_method(function.snake_case) do |*args|
    if valid_args? and valid_args?.call(*args)               
      api.call *args
    else
      // raise argument error, etc
    end                                         
  end

end

Then

def_api 'FindWindow', ['P', 'P'], 'L', { |args| return true if // API specific checks succeed }

UPDATE: Adding more color at questioner's request

The &valid_args? is the name of a block parameter. The ampersand prefix (&) is how you tell ruby that you passing a block. You can only pass one block to a methodand it must be the last parameter in the argument list. The question mark suffix (?) is just a convention in Ruby programming for naming functions which return boolean values.

If a block is passed you invoke it with &block.call(args)

To call a method with a block argument you do something like this:

method { |args| // block implementation }

or

method do |args|
  // block implementation
end

The args are passed to the block through the call method. Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文